/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 1999-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sun.org.apache.xerces.internal.impl.xs.traversers;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Stack;
import java.util.Vector;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import com.sun.org.apache.xerces.internal.impl.Constants;
import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
import com.sun.org.apache.xerces.internal.impl.dv.SchemaDVFactory;
import com.sun.org.apache.xerces.internal.impl.dv.xs.XSSimpleTypeDecl;
import com.sun.org.apache.xerces.internal.impl.xs.SchemaGrammar;
import com.sun.org.apache.xerces.internal.impl.xs.SchemaNamespaceSupport;
import com.sun.org.apache.xerces.internal.impl.xs.SchemaSymbols;
import com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaException;
import com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader;
import com.sun.org.apache.xerces.internal.impl.xs.XSAttributeDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSAttributeGroupDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSComplexTypeDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSDDescription;
import com.sun.org.apache.xerces.internal.impl.xs.XSDeclarationPool;
import com.sun.org.apache.xerces.internal.impl.xs.XSElementDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSGrammarBucket;
import com.sun.org.apache.xerces.internal.impl.xs.XSGroupDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSMessageFormatter;
import com.sun.org.apache.xerces.internal.impl.xs.XSModelGroupImpl;
import com.sun.org.apache.xerces.internal.impl.xs.XSNotationDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSParticleDecl;
import com.sun.org.apache.xerces.internal.impl.xs.identity.IdentityConstraint;
import com.sun.org.apache.xerces.internal.impl.xs.opti.ElementImpl;
import com.sun.org.apache.xerces.internal.impl.xs.opti.SchemaDOMParser;
import com.sun.org.apache.xerces.internal.impl.xs.opti.SchemaParsingConfig;
import com.sun.org.apache.xerces.internal.impl.xs.util.SimpleLocator;
import com.sun.org.apache.xerces.internal.impl.xs.util.XSInputSource;
import com.sun.org.apache.xerces.internal.parsers.SAXParser;
import com.sun.org.apache.xerces.internal.parsers.XML11Configuration;
import com.sun.org.apache.xerces.internal.util.DOMInputSource;
import com.sun.org.apache.xerces.internal.util.DOMUtil;
import com.sun.org.apache.xerces.internal.util.DefaultErrorHandler;
import com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper;
import com.sun.org.apache.xerces.internal.util.SAXInputSource;
import com.sun.org.apache.xerces.internal.util.StAXInputSource;
import com.sun.org.apache.xerces.internal.util.StAXLocationWrapper;
import com.sun.org.apache.xerces.internal.util.SymbolHash;
import com.sun.org.apache.xerces.internal.util.SymbolTable;
import com.sun.org.apache.xerces.internal.util.XMLSymbols;
import com.sun.org.apache.xerces.internal.util.URI.MalformedURIException;
import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
import com.sun.org.apache.xerces.internal.xni.QName;
import com.sun.org.apache.xerces.internal.xni.XNIException;
import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool;
import com.sun.org.apache.xerces.internal.xni.grammars.XMLSchemaDescription;
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
import com.sun.org.apache.xerces.internal.xni.parser.XMLParseException;
import com.sun.org.apache.xerces.internal.xs.StringList;
import com.sun.org.apache.xerces.internal.xs.XSAttributeDeclaration;
import com.sun.org.apache.xerces.internal.xs.XSAttributeGroupDefinition;
import com.sun.org.apache.xerces.internal.xs.XSAttributeUse;
import com.sun.org.apache.xerces.internal.xs.XSConstants;
import com.sun.org.apache.xerces.internal.xs.XSElementDeclaration;
import com.sun.org.apache.xerces.internal.xs.XSModelGroup;
import com.sun.org.apache.xerces.internal.xs.XSModelGroupDefinition;
import com.sun.org.apache.xerces.internal.xs.XSNamedMap;
import com.sun.org.apache.xerces.internal.xs.XSObject;
import com.sun.org.apache.xerces.internal.xs.XSObjectList;
import com.sun.org.apache.xerces.internal.xs.XSParticle;
import com.sun.org.apache.xerces.internal.xs.XSSimpleTypeDefinition;
import com.sun.org.apache.xerces.internal.xs.XSTerm;
import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
import com.sun.org.apache.xerces.internal.xs.datatypes.ObjectList;
import javax.xml.XMLConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * The purpose of this class is to co-ordinate the construction of a
 * grammar object corresponding to a schema.  To do this, it must be
 * prepared to parse several schema documents (for instance if the
 * schema document originally referred to contains <include> or
 * <redefined> information items).  If any of the schemas imports a
 * schema, other grammars may be constructed as a side-effect.
 *
 * @author Neil Graham, IBM
 * @author Pavani Mukthipudi, Sun Microsystems
 * @version $Id: XSDHandler.java,v 1.9 2010-11-01 04:40:02 joehw Exp $
 * @xerces.internal
 */
public class XSDHandler {

  /**
   * Feature identifier: validation.
   */
  protected static final String VALIDATION =
      Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;

  /**
   * feature identifier: XML Schema validation
   */
  protected static final String XMLSCHEMA_VALIDATION =
      Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE;

  /**
   * Feature identifier:  allow java encodings
   */
  protected static final String ALLOW_JAVA_ENCODINGS =
      Constants.XERCES_FEATURE_PREFIX + Constants.ALLOW_JAVA_ENCODINGS_FEATURE;

  /**
   * Feature identifier:  continue after fatal error
   */
  protected static final String CONTINUE_AFTER_FATAL_ERROR =
      Constants.XERCES_FEATURE_PREFIX + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE;

  /**
   * Feature identifier:  allow java encodings
   */
  protected static final String STANDARD_URI_CONFORMANT_FEATURE =
      Constants.XERCES_FEATURE_PREFIX + Constants.STANDARD_URI_CONFORMANT_FEATURE;

  /**
   * Feature: disallow doctype
   */
  protected static final String DISALLOW_DOCTYPE =
      Constants.XERCES_FEATURE_PREFIX + Constants.DISALLOW_DOCTYPE_DECL_FEATURE;

  /**
   * Feature: generate synthetic annotations
   */
  protected static final String GENERATE_SYNTHETIC_ANNOTATIONS =
      Constants.XERCES_FEATURE_PREFIX + Constants.GENERATE_SYNTHETIC_ANNOTATIONS_FEATURE;

  /**
   * Feature identifier: validate annotations.
   */
  protected static final String VALIDATE_ANNOTATIONS =
      Constants.XERCES_FEATURE_PREFIX + Constants.VALIDATE_ANNOTATIONS_FEATURE;

  /**
   * Feature identifier: honour all schemaLocations
   */
  protected static final String HONOUR_ALL_SCHEMALOCATIONS =
      Constants.XERCES_FEATURE_PREFIX + Constants.HONOUR_ALL_SCHEMALOCATIONS_FEATURE;

  /**
   * Feature identifier: namespace growth
   */
  protected static final String NAMESPACE_GROWTH =
      Constants.XERCES_FEATURE_PREFIX + Constants.NAMESPACE_GROWTH_FEATURE;

  /**
   * Feature identifier: tolerate duplicates
   */
  protected static final String TOLERATE_DUPLICATES =
      Constants.XERCES_FEATURE_PREFIX + Constants.TOLERATE_DUPLICATES_FEATURE;

  /**
   * Feature identifier: namespace prefixes.
   */
  private static final String NAMESPACE_PREFIXES =
      Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACE_PREFIXES_FEATURE;

  /**
   * Feature identifier: string interning.
   */
  protected static final String STRING_INTERNING =
      Constants.SAX_FEATURE_PREFIX + Constants.STRING_INTERNING_FEATURE;

  /**
   * Property identifier: error handler.
   */
  protected static final String ERROR_HANDLER =
      Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY;

  /**
   * Property identifier: JAXP schema source.
   */
  protected static final String JAXP_SCHEMA_SOURCE =
      Constants.JAXP_PROPERTY_PREFIX + Constants.SCHEMA_SOURCE;

  /**
   * Property identifier: entity resolver.
   */
  public static final String ENTITY_RESOLVER =
      Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
  /**
   * Property identifier: entity manager.
   */
  protected static final String ENTITY_MANAGER =
      Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_MANAGER_PROPERTY;

  /**
   * Property identifier: error reporter.
   */
  public static final String ERROR_REPORTER =
      Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;

  /**
   * Property identifier: grammar pool.
   */
  public static final String XMLGRAMMAR_POOL =
      Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY;

  /**
   * Property identifier: symbol table.
   */
  public static final String SYMBOL_TABLE =
      Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;

  /**
   * Property identifier: security manager.
   */
  protected static final String SECURITY_MANAGER =
      Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;

  private static final String SECURE_PROCESSING =
      Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;

  /**
   * Property identifier: locale.
   */
  protected static final String LOCALE =
      Constants.XERCES_PROPERTY_PREFIX + Constants.LOCALE_PROPERTY;

  /**
   * Property identifier: Security property manager.
   */
  private static final String XML_SECURITY_PROPERTY_MANAGER =
      Constants.XML_SECURITY_PROPERTY_MANAGER;

  protected static final boolean DEBUG_NODE_POOL = false;

  // Data

  // different sorts of declarations; should make lookup and
  // traverser calling more efficient/less bulky.
  final static int ATTRIBUTE_TYPE = 1;
  final static int ATTRIBUTEGROUP_TYPE = 2;
  final static int ELEMENT_TYPE = 3;
  final static int GROUP_TYPE = 4;
  final static int IDENTITYCONSTRAINT_TYPE = 5;
  final static int NOTATION_TYPE = 6;
  final static int TYPEDECL_TYPE = 7;

  // this string gets appended to redefined names; it's purpose is to be
  // as unlikely as possible to cause collisions.
  public final static String REDEF_IDENTIFIER = "_fn3dktizrknc9pi";

  //
  //protected data that can be accessable by any traverser

  protected XSDeclarationPool fDeclPool = null;

  /**
   * <p>Security manager in effect.</p>
   *
   * <p>Protected to allow access by any traverser.</p>
   */
  protected XMLSecurityManager fSecureProcessing = null;

  private String fAccessExternalSchema;
  private String fAccessExternalDTD;

  // These tables correspond to the symbol spaces defined in the
  // spec.
  // They are keyed with a QName (that is, String("URI,localpart) and
  // their values are nodes corresponding to the given name's decl.
  // By asking the node for its ownerDocument and looking in
  // XSDocumentInfoRegistry we can easily get the corresponding
  // XSDocumentInfo object.
  private boolean registryEmpty = true;
  private Map<String, Element> fUnparsedAttributeRegistry = new HashMap();
  private Map<String, Element> fUnparsedAttributeGroupRegistry = new HashMap();
  private Map<String, Element> fUnparsedElementRegistry = new HashMap();
  private Map<String, Element> fUnparsedGroupRegistry = new HashMap();
  private Map<String, Element> fUnparsedIdentityConstraintRegistry = new HashMap();
  private Map<String, Element> fUnparsedNotationRegistry = new HashMap();
  private Map<String, Element> fUnparsedTypeRegistry = new HashMap();
  // Compensation for the above hashtables to locate XSDocumentInfo,
  // Since we may take Schema Element directly, so can not get the
  // corresponding XSDocumentInfo object just using above hashtables.
  private Map<String, XSDocumentInfo> fUnparsedAttributeRegistrySub = new HashMap();
  private Map<String, XSDocumentInfo> fUnparsedAttributeGroupRegistrySub = new HashMap();
  private Map<String, XSDocumentInfo> fUnparsedElementRegistrySub = new HashMap();
  private Map<String, XSDocumentInfo> fUnparsedGroupRegistrySub = new HashMap();
  private Map<String, XSDocumentInfo> fUnparsedIdentityConstraintRegistrySub = new HashMap();
  private Map<String, XSDocumentInfo> fUnparsedNotationRegistrySub = new HashMap();
  private Map<String, XSDocumentInfo> fUnparsedTypeRegistrySub = new HashMap();

  // Stores XSDocumentInfo (keyed by component name), to check for duplicate
  // components declared within the same xsd document
  private Map fUnparsedRegistriesExt[] = new HashMap[]{
      null,
      null, // ATTRIBUTE_TYPE
      null, // ATTRIBUTEGROUP_TYPE
      null, // ELEMENT_TYPE
      null, // GROUP_TYPE
      null, // IDENTITYCONSTRAINT_TYPE
      null, // NOTATION_TYPE
      null, // TYPEDECL_TYPE
  };

  // this hashtable is keyed on by XSDocumentInfo objects.  Its values
  // are Vectors containing the XSDocumentInfo objects <include>d,
  // <import>ed or <redefine>d by the key XSDocumentInfo.
  private Map<XSDocumentInfo, Vector> fDependencyMap = new HashMap();

  // this hashtable is keyed on by a target namespace.  Its values
  // are Vectors containing namespaces imported by schema documents
  // with the key target namespace.
  // if an imprted schema has absent namespace, the value "null" is stored.
  private Map<String, Vector> fImportMap = new HashMap();
  // all namespaces that imports other namespaces
  // if the importing schema has absent namespace, empty string is stored.
  // (because the key of a hashtable can't be null.)
  private Vector fAllTNSs = new Vector();
  // stores instance document mappings between namespaces and schema hints
  private Map fLocationPairs = null;
  private static final Map EMPTY_TABLE = new HashMap();

  // Records which nodes are hidden when the input is a DOMInputSource.
  Hashtable fHiddenNodes = null;

  // convenience methods
  private String null2EmptyString(String ns) {
    return ns == null ? XMLSymbols.EMPTY_STRING : ns;
  }

  private String emptyString2Null(String ns) {
    return ns == XMLSymbols.EMPTY_STRING ? null : ns;
  }

  // use Schema Element to lookup the SystemId.
  private String doc2SystemId(Element ele) {
    String documentURI = null;
    /**
     * REVISIT: Casting until DOM Level 3 interfaces are available. -- mrglavas
     */
    if (ele
        .getOwnerDocument() instanceof com.sun.org.apache.xerces.internal.impl.xs.opti.SchemaDOM) {
      documentURI = ((com.sun.org.apache.xerces.internal.impl.xs.opti.SchemaDOM) ele
          .getOwnerDocument()).getDocumentURI();
    }
    return documentURI != null ? documentURI : (String) fDoc2SystemId.get(ele);
  }

  // This vector stores strings which are combinations of the
  // publicId and systemId of the inputSource corresponding to a
  // schema document.  This combination is used so that the user's
  // EntityResolver can provide a consistent way of identifying a
  // schema document that is included in multiple other schemas.
  private Map fTraversed = new HashMap();

  // this hashtable contains a mapping from Schema Element to its systemId
  // this is useful to resolve a uri relative to the referring document
  private Map fDoc2SystemId = new HashMap();

  // the primary XSDocumentInfo we were called to parse
  private XSDocumentInfo fRoot = null;

  // This hashtable's job is to act as a link between the Schema Element and its
  // XSDocumentInfo object.
  private Map fDoc2XSDocumentMap = new HashMap();

  // map between <redefine> elements and the XSDocumentInfo
  // objects that correspond to the documents being redefined.
  private Map fRedefine2XSDMap = null;

  // map between <redefine> elements and the namespace support
  private Map fRedefine2NSSupport = null;

  // these objects store a mapping between the names of redefining
  // groups/attributeGroups and the groups/AttributeGroups which
  // they redefine by restriction (implicitly).  It is up to the
  // Group and AttributeGroup traversers to check these restrictions for
  // validity.
  private Map fRedefinedRestrictedAttributeGroupRegistry = new HashMap();
  private Map fRedefinedRestrictedGroupRegistry = new HashMap();

  // a variable storing whether the last schema document
  // processed (by getSchema) was a duplicate.
  private boolean fLastSchemaWasDuplicate;

  // validate annotations feature
  private boolean fValidateAnnotations = false;

  //handle multiple import feature
  private boolean fHonourAllSchemaLocations = false;

  //handle namespace growth feature
  boolean fNamespaceGrowth = false;

  // handle tolerate duplicates feature
  boolean fTolerateDuplicates = false;

  // the XMLErrorReporter
  private XMLErrorReporter fErrorReporter;
  private XMLEntityResolver fEntityResolver;

  // the XSAttributeChecker
  private XSAttributeChecker fAttributeChecker;

  // the symbol table
  private SymbolTable fSymbolTable;

  // the GrammarResolver
  private XSGrammarBucket fGrammarBucket;

  // the Grammar description
  private XSDDescription fSchemaGrammarDescription;

  // the Grammar Pool
  private XMLGrammarPool fGrammarPool;

  //************ Traversers **********
  XSDAttributeGroupTraverser fAttributeGroupTraverser;
  XSDAttributeTraverser fAttributeTraverser;
  XSDComplexTypeTraverser fComplexTypeTraverser;
  XSDElementTraverser fElementTraverser;
  XSDGroupTraverser fGroupTraverser;
  XSDKeyrefTraverser fKeyrefTraverser;
  XSDNotationTraverser fNotationTraverser;
  XSDSimpleTypeTraverser fSimpleTypeTraverser;
  XSDUniqueOrKeyTraverser fUniqueOrKeyTraverser;
  XSDWildcardTraverser fWildCardTraverser;

  SchemaDVFactory fDVFactory;
  SchemaDOMParser fSchemaParser;
  SchemaContentHandler fXSContentHandler;
  StAXSchemaParser fStAXSchemaParser;
  XML11Configuration fAnnotationValidator;
  XSAnnotationGrammarPool fGrammarBucketAdapter;

  // these data members are needed for the deferred traversal
  // of local elements.

  // the initial size of the array to store deferred local elements
  private static final int INIT_STACK_SIZE = 30;
  // the incremental size of the array to store deferred local elements
  private static final int INC_STACK_SIZE = 10;
  // current position of the array (# of deferred local elements)
  private int fLocalElemStackPos = 0;

  private XSParticleDecl[] fParticle = new XSParticleDecl[INIT_STACK_SIZE];
  private Element[] fLocalElementDecl = new Element[INIT_STACK_SIZE];
  private XSDocumentInfo[] fLocalElementDecl_schema = new XSDocumentInfo[INIT_STACK_SIZE]; //JACK
  private int[] fAllContext = new int[INIT_STACK_SIZE];
  private XSObject[] fParent = new XSObject[INIT_STACK_SIZE];
  private String[][] fLocalElemNamespaceContext = new String[INIT_STACK_SIZE][1];

  // these data members are needed for the deferred traversal
  // of keyrefs.

  // the initial size of the array to store deferred keyrefs
  private static final int INIT_KEYREF_STACK = 2;
  // the incremental size of the array to store deferred keyrefs
  private static final int INC_KEYREF_STACK_AMOUNT = 2;
  // current position of the array (# of deferred keyrefs)
  private int fKeyrefStackPos = 0;

  private Element[] fKeyrefs = new Element[INIT_KEYREF_STACK];
  private XSDocumentInfo[] fKeyrefsMapXSDocumentInfo = new XSDocumentInfo[INIT_KEYREF_STACK];
  private XSElementDecl[] fKeyrefElems = new XSElementDecl[INIT_KEYREF_STACK];
  private String[][] fKeyrefNamespaceContext = new String[INIT_KEYREF_STACK][1];

  // global decls: map from decl name to decl object
  SymbolHash fGlobalAttrDecls = new SymbolHash();
  SymbolHash fGlobalAttrGrpDecls = new SymbolHash();
  SymbolHash fGlobalElemDecls = new SymbolHash();
  SymbolHash fGlobalGroupDecls = new SymbolHash();
  SymbolHash fGlobalNotationDecls = new SymbolHash();
  SymbolHash fGlobalIDConstraintDecls = new SymbolHash();
  SymbolHash fGlobalTypeDecls = new SymbolHash();

  // Constructors
  public XSDHandler() {
    fHiddenNodes = new Hashtable();
    fSchemaParser = new SchemaDOMParser(new SchemaParsingConfig());
  }

  // it should be possible to use the same XSDHandler to parse
  // multiple schema documents; this will allow one to be
  // constructed.
  public XSDHandler(XSGrammarBucket gBucket) {
    this();
    fGrammarBucket = gBucket;

    // Note: don't use SchemaConfiguration internally
    //       we will get stack overflaw because
    //       XMLSchemaValidator will be instantiating XSDHandler...
    fSchemaGrammarDescription = new XSDDescription();
  } // end constructor

  /**
   * This method initiates the parse of a schema.  It will likely be
   * called from the Validator and it will make the
   * resulting grammar available; it returns a reference to this object just
   * in case.  A reset(XMLComponentManager) must be called before this methods is called.
   *
   * @return the SchemaGrammar
   */
  public SchemaGrammar parseSchema(XMLInputSource is, XSDDescription desc,
      Map locationPairs)
      throws IOException {
    fLocationPairs = locationPairs;
    fSchemaParser.resetNodePool();
    SchemaGrammar grammar = null;
    String schemaNamespace = null;
    short referType = desc.getContextType();

    // if loading using JAXP schemaSource property, or using grammar caching loadGrammar
    // the desc.targetNamespace is always null.
    // Therefore we should not attempt to find out if
    // the schema is already in the bucket, since in the case we have
    // no namespace schema in the bucket, findGrammar will always return the
    // no namespace schema.
    if (referType != XSDDescription.CONTEXT_PREPARSE) {
      // first try to find it in the bucket/pool, return if one is found
      if (fHonourAllSchemaLocations && referType == XSDDescription.CONTEXT_IMPORT
          && isExistingGrammar(desc, fNamespaceGrowth)) {
        grammar = fGrammarBucket.getGrammar(desc.getTargetNamespace());
      } else {
        grammar = findGrammar(desc, fNamespaceGrowth);
      }
      if (grammar != null) {
        if (!fNamespaceGrowth) {
          return grammar;
        } else {
          try {
            if (grammar.getDocumentLocations().contains(
                XMLEntityManager.expandSystemId(is.getSystemId(), is.getBaseSystemId(), false))) {
              return grammar;
            }
          } catch (MalformedURIException e) {
            //REVISIT: return the grammar?
          }
        }
      }

      schemaNamespace = desc.getTargetNamespace();
      // handle empty string URI as null
      if (schemaNamespace != null) {
        schemaNamespace = fSymbolTable.addSymbol(schemaNamespace);
      }
    }

    // before parsing a schema, need to clear registries associated with
    // parsing schemas
    prepareForParse();

    Element schemaRoot = null;
    // first phase:  construct trees.
    if (is instanceof DOMInputSource) {
      schemaRoot = getSchemaDocument(schemaNamespace, (DOMInputSource) is,
          referType == XSDDescription.CONTEXT_PREPARSE,
          referType, null);
    } // DOMInputSource
    else if (is instanceof SAXInputSource) {
      schemaRoot = getSchemaDocument(schemaNamespace, (SAXInputSource) is,
          referType == XSDDescription.CONTEXT_PREPARSE,
          referType, null);
    } // SAXInputSource
    else if (is instanceof StAXInputSource) {
      schemaRoot = getSchemaDocument(schemaNamespace, (StAXInputSource) is,
          referType == XSDDescription.CONTEXT_PREPARSE,
          referType, null);
    } // StAXInputSource
    else if (is instanceof XSInputSource) {
      schemaRoot = getSchemaDocument((XSInputSource) is, desc);
    } // XSInputSource
    else {
      schemaRoot = getSchemaDocument(schemaNamespace, is,
          referType == XSDDescription.CONTEXT_PREPARSE,
          referType, null);

    } //is instanceof XMLInputSource

    if (schemaRoot == null) {
      // something went wrong right off the hop
      if (is instanceof XSInputSource) {
        return fGrammarBucket.getGrammar(desc.getTargetNamespace());
      }
      return grammar;
    }

    if (referType == XSDDescription.CONTEXT_PREPARSE) {
      Element schemaElem = schemaRoot;
      schemaNamespace = DOMUtil.getAttrValue(schemaElem, SchemaSymbols.ATT_TARGETNAMESPACE);
      if (schemaNamespace != null && schemaNamespace.length() > 0) {
        // Since now we've discovered a namespace, we need to update xsd key
        // and store this schema in traversed schemas bucket
        schemaNamespace = fSymbolTable.addSymbol(schemaNamespace);
        desc.setTargetNamespace(schemaNamespace);
      } else {
        schemaNamespace = null;
      }
      grammar = findGrammar(desc, fNamespaceGrowth);
      String schemaId = XMLEntityManager
          .expandSystemId(is.getSystemId(), is.getBaseSystemId(), false);
      if (grammar != null) {
        // When namespace growth is enabled and a null location is provided we cannot tell
        // whether we've loaded this schema document before so we must assume that we haven't.
        if (!fNamespaceGrowth || (schemaId != null && grammar.getDocumentLocations()
            .contains(schemaId))) {
          return grammar;
        }
      }

      XSDKey key = new XSDKey(schemaId, referType, schemaNamespace);
      fTraversed.put(key, schemaRoot);
      if (schemaId != null) {
        fDoc2SystemId.put(schemaRoot, schemaId);
      }
    }

    // before constructing trees and traversing a schema, need to reset
    // all traversers and clear all registries
    prepareForTraverse();

    fRoot = constructTrees(schemaRoot, is.getSystemId(), desc, grammar != null);
    if (fRoot == null) {
      return null;
    }

    // second phase:  fill global registries.
    buildGlobalNameRegistries();

    // third phase:  call traversers
    ArrayList annotationInfo = fValidateAnnotations ? new ArrayList() : null;
    traverseSchemas(annotationInfo);

    // fourth phase: handle local element decls
    traverseLocalElements();

    // fifth phase:  handle Keyrefs
    resolveKeyRefs();

    // sixth phase:  validate attribute of non-schema namespaces
    // REVISIT: skip this for now. we really don't want to do it.
    //fAttributeChecker.checkNonSchemaAttributes(fGrammarBucket);

    // seventh phase:  store imported grammars
    // for all grammars with <import>s
    for (int i = fAllTNSs.size() - 1; i >= 0; i--) {
      // get its target namespace
      String tns = (String) fAllTNSs.elementAt(i);
      // get all namespaces it imports
      Vector ins = (Vector) fImportMap.get(tns);
      // get the grammar
      SchemaGrammar sg = fGrammarBucket.getGrammar(emptyString2Null(tns));
      if (sg == null) {
        continue;
      }
      SchemaGrammar isg;
      // for imported namespace
      int count = 0;
      for (int j = 0; j < ins.size(); j++) {
        // get imported grammar
        isg = fGrammarBucket.getGrammar((String) ins.elementAt(j));
        // reuse the same vector
        if (isg != null) {
          ins.setElementAt(isg, count++);
        }
      }
      ins.setSize(count);
      // set the imported grammars
      sg.setImportedGrammars(ins);
    }

    /** validate annotations **/
    if (fValidateAnnotations && annotationInfo.size() > 0) {
      validateAnnotations(annotationInfo);
    }

    // and return.
    return fGrammarBucket.getGrammar(fRoot.fTargetNamespace);
  } // end parseSchema

  private void validateAnnotations(ArrayList annotationInfo) {
    if (fAnnotationValidator == null) {
      createAnnotationValidator();
    }
    final int size = annotationInfo.size();
    final XMLInputSource src = new XMLInputSource(null, null, null);
    fGrammarBucketAdapter.refreshGrammars(fGrammarBucket);
    for (int i = 0; i < size; i += 2) {
      src.setSystemId((String) annotationInfo.get(i));
      XSAnnotationInfo annotation = (XSAnnotationInfo) annotationInfo.get(i + 1);
      while (annotation != null) {
        src.setCharacterStream(new StringReader(annotation.fAnnotation));
        try {
          fAnnotationValidator.parse(src);
        } catch (IOException exc) {
        }
        annotation = annotation.next;
      }
    }
  }

  private void createAnnotationValidator() {
    fAnnotationValidator = new XML11Configuration();
    fGrammarBucketAdapter = new XSAnnotationGrammarPool();
    fAnnotationValidator.setFeature(VALIDATION, true);
    fAnnotationValidator.setFeature(XMLSCHEMA_VALIDATION, true);
    fAnnotationValidator.setProperty(XMLGRAMMAR_POOL, fGrammarBucketAdapter);
    /** Set error handler. **/
    XMLErrorHandler errorHandler = fErrorReporter.getErrorHandler();
    fAnnotationValidator.setProperty(ERROR_HANDLER,
        (errorHandler != null) ? errorHandler : new DefaultErrorHandler());
    /** Set locale. **/
    Locale locale = fErrorReporter.getLocale();
    fAnnotationValidator.setProperty(LOCALE, locale);
  }

  /**
   * Pull the grammar out of the bucket simply using
   * its TNS as a key
   */
  SchemaGrammar getGrammar(String tns) {
    return fGrammarBucket.getGrammar(tns);
  }

  /**
   * First try to find a grammar in the bucket, if failed, consult the
   * grammar pool. If a grammar is found in the pool, then add it (and all
   * imported ones) into the bucket.
   */
  protected SchemaGrammar findGrammar(XSDDescription desc, boolean ignoreConflict) {
    SchemaGrammar sg = fGrammarBucket.getGrammar(desc.getTargetNamespace());
    if (sg == null) {
      if (fGrammarPool != null) {
        sg = (SchemaGrammar) fGrammarPool.retrieveGrammar(desc);
        if (sg != null) {
          // put this grammar into the bucket, along with grammars
          // imported by it (directly or indirectly)
          if (!fGrammarBucket.putGrammar(sg, true, ignoreConflict)) {
            // REVISIT: a conflict between new grammar(s) and grammars
            // in the bucket. What to do? A warning? An exception?
            reportSchemaWarning("GrammarConflict", null, null);
            sg = null;
          }
        }
      }
    }
    return sg;
  }

  // may wish to have setter methods for ErrorHandler,
  // EntityResolver...

  private static final String[][] NS_ERROR_CODES = {
      {"src-include.2.1", "src-include.2.1"},
      {"src-redefine.3.1", "src-redefine.3.1"},
      {"src-import.3.1", "src-import.3.2"},
      null,
      {"TargetNamespace.1", "TargetNamespace.2"},
      {"TargetNamespace.1", "TargetNamespace.2"},
      {"TargetNamespace.1", "TargetNamespace.2"},
      {"TargetNamespace.1", "TargetNamespace.2"}
  };

  private static final String[] ELE_ERROR_CODES = {
      "src-include.1", "src-redefine.2", "src-import.2", "schema_reference.4",
      "schema_reference.4", "schema_reference.4", "schema_reference.4", "schema_reference.4"
  };

  // This method does several things:
  // It constructs an instance of an XSDocumentInfo object using the
  // schemaRoot node.  Then, for each <include>,
  // <redefine>, and <import> children, it attempts to resolve the
  // requested schema document, initiates a DOM parse, and calls
  // itself recursively on that document's root.  It also records in
  // the DependencyMap object what XSDocumentInfo objects its XSDocumentInfo
  // depends on.
  // It also makes sure the targetNamespace of the schema it was
  // called to parse is correct.
  protected XSDocumentInfo constructTrees(Element schemaRoot, String locationHint,
      XSDDescription desc, boolean nsCollision) {
    if (schemaRoot == null) {
      return null;
    }
    String callerTNS = desc.getTargetNamespace();
    short referType = desc.getContextType();

    XSDocumentInfo currSchemaInfo = null;
    try {
      // note that attributes are freed at end of traverseSchemas()
      currSchemaInfo = new XSDocumentInfo(schemaRoot, fAttributeChecker, fSymbolTable);
    } catch (XMLSchemaException se) {
      reportSchemaError(ELE_ERROR_CODES[referType],
          new Object[]{locationHint},
          schemaRoot);
      return null;
    }
    // targetNamespace="" is not valid, issue a warning, and ignore it
    if (currSchemaInfo.fTargetNamespace != null &&
        currSchemaInfo.fTargetNamespace.length() == 0) {
      reportSchemaWarning("EmptyTargetNamespace",
          new Object[]{locationHint},
          schemaRoot);
      currSchemaInfo.fTargetNamespace = null;
    }

    if (callerTNS != null) {
      // the second index to the NS_ERROR_CODES array
      // if the caller/expected NS is not absent, we use the first column
      int secondIdx = 0;
      // for include and redefine
      if (referType == XSDDescription.CONTEXT_INCLUDE ||
          referType == XSDDescription.CONTEXT_REDEFINE) {
        // if the referred document has no targetNamespace,
        // it's a chameleon schema
        if (currSchemaInfo.fTargetNamespace == null) {
          currSchemaInfo.fTargetNamespace = callerTNS;
          currSchemaInfo.fIsChameleonSchema = true;
        }
        // if the referred document has a target namespace differing
        // from the caller, it's an error
        else if (callerTNS != currSchemaInfo.fTargetNamespace) {
          reportSchemaError(NS_ERROR_CODES[referType][secondIdx],
              new Object[]{callerTNS, currSchemaInfo.fTargetNamespace},
              schemaRoot);
          return null;
        }
      }
      // for instance and import, the two NS's must be the same
      else if (referType != XSDDescription.CONTEXT_PREPARSE
          && callerTNS != currSchemaInfo.fTargetNamespace) {
        reportSchemaError(NS_ERROR_CODES[referType][secondIdx],
            new Object[]{callerTNS, currSchemaInfo.fTargetNamespace},
            schemaRoot);
        return null;
      }
    }
    // now there is no caller/expected NS, it's an error for the referred
    // document to have a target namespace, unless we are preparsing a schema
    else if (currSchemaInfo.fTargetNamespace != null) {
      // set the target namespace of the description
      if (referType == XSDDescription.CONTEXT_PREPARSE) {
        desc.setTargetNamespace(currSchemaInfo.fTargetNamespace);
        callerTNS = currSchemaInfo.fTargetNamespace;
      } else {
        // the second index to the NS_ERROR_CODES array
        // if the caller/expected NS is absent, we use the second column
        int secondIdx = 1;
        reportSchemaError(NS_ERROR_CODES[referType][secondIdx],
            new Object[]{callerTNS, currSchemaInfo.fTargetNamespace},
            schemaRoot);
        return null;
      }
    }
    // the other cases (callerTNS == currSchemaInfo.fTargetNamespce == null)
    // are valid

    // a schema document can always access it's own target namespace
    currSchemaInfo.addAllowedNS(currSchemaInfo.fTargetNamespace);

    SchemaGrammar sg = null;

    // we have a namespace collision
    if (nsCollision) {
      SchemaGrammar sg2 = fGrammarBucket.getGrammar(currSchemaInfo.fTargetNamespace);
      if (sg2.isImmutable()) {
        sg = new SchemaGrammar(sg2);
        fGrammarBucket.putGrammar(sg);
        // update all the grammars in the bucket to point to the new grammar.
        updateImportListWith(sg);
      } else {
        sg = sg2;
      }

      // update import list of the new grammar
      updateImportListFor(sg);
    } else if (referType == XSDDescription.CONTEXT_INCLUDE ||
        referType == XSDDescription.CONTEXT_REDEFINE) {
      sg = fGrammarBucket.getGrammar(currSchemaInfo.fTargetNamespace);
    } else if (fHonourAllSchemaLocations && referType == XSDDescription.CONTEXT_IMPORT) {
      sg = findGrammar(desc, false);
      if (sg == null) {
        sg = new SchemaGrammar(currSchemaInfo.fTargetNamespace, desc.makeClone(), fSymbolTable);
        fGrammarBucket.putGrammar(sg);
      }
    } else {
      sg = new SchemaGrammar(currSchemaInfo.fTargetNamespace, desc.makeClone(), fSymbolTable);
      fGrammarBucket.putGrammar(sg);
    }

    // store the document and its location
    // REVISIT: don't expose the DOM tree
    sg.addDocument(null, (String) fDoc2SystemId.get(currSchemaInfo.fSchemaElement));

    fDoc2XSDocumentMap.put(schemaRoot, currSchemaInfo);
    Vector dependencies = new Vector();
    Element rootNode = schemaRoot;

    Element newSchemaRoot = null;
    for (Element child = DOMUtil.getFirstChildElement(rootNode);
        child != null;
        child = DOMUtil.getNextSiblingElement(child)) {
      String schemaNamespace = null;
      String schemaHint = null;
      String localName = DOMUtil.getLocalName(child);

      short refType = -1;
      boolean importCollision = false;

      if (localName.equals(SchemaSymbols.ELT_ANNOTATION)) {
        continue;
      } else if (localName.equals(SchemaSymbols.ELT_IMPORT)) {
        refType = XSDDescription.CONTEXT_IMPORT;
        // have to handle some validation here too!
        // call XSAttributeChecker to fill in attrs
        Object[] importAttrs = fAttributeChecker.checkAttributes(child, true, currSchemaInfo);
        schemaHint = (String) importAttrs[XSAttributeChecker.ATTIDX_SCHEMALOCATION];
        schemaNamespace = (String) importAttrs[XSAttributeChecker.ATTIDX_NAMESPACE];
        if (schemaNamespace != null) {
          schemaNamespace = fSymbolTable.addSymbol(schemaNamespace);
        }

        // check contents and process optional annotations
        Element importChild = DOMUtil.getFirstChildElement(child);
        if (importChild != null) {
          String importComponentType = DOMUtil.getLocalName(importChild);
          if (importComponentType.equals(SchemaSymbols.ELT_ANNOTATION)) {
            // promoting annotations to parent component
            sg.addAnnotation(
                fElementTraverser
                    .traverseAnnotationDecl(importChild, importAttrs, true, currSchemaInfo));
          } else {
            reportSchemaError("s4s-elt-must-match.1",
                new Object[]{localName, "annotation?", importComponentType}, child);
          }
          if (DOMUtil.getNextSiblingElement(importChild) != null) {
            reportSchemaError("s4s-elt-must-match.1", new Object[]{localName, "annotation?",
                DOMUtil.getLocalName(DOMUtil.getNextSiblingElement(importChild))}, child);
          }
        } else {
          String text = DOMUtil.getSyntheticAnnotation(child);
          if (text != null) {
            sg.addAnnotation(fElementTraverser
                .traverseSyntheticAnnotation(child, text, importAttrs, true, currSchemaInfo));
          }
        }
        fAttributeChecker.returnAttrArray(importAttrs, currSchemaInfo);

        // a document can't import another document with the same namespace
        if (schemaNamespace == currSchemaInfo.fTargetNamespace) {
          reportSchemaError(schemaNamespace != null ?
              "src-import.1.1" : "src-import.1.2", new Object[]{schemaNamespace}, child);
          continue;
        }

        // if this namespace has not been imported by this document,
        //  then import if multiple imports support is enabled.
        if (currSchemaInfo.isAllowedNS(schemaNamespace)) {
          if (!fHonourAllSchemaLocations && !fNamespaceGrowth) {
            continue;
          }
        } else {
          currSchemaInfo.addAllowedNS(schemaNamespace);
        }
        // also record the fact that one namespace imports another one
        // convert null to ""
        String tns = null2EmptyString(currSchemaInfo.fTargetNamespace);
        // get all namespaces imported by this one
        Vector ins = (Vector) fImportMap.get(tns);
        // if no namespace was imported, create new Vector
        if (ins == null) {
          // record that this one imports other(s)
          fAllTNSs.addElement(tns);
          ins = new Vector();
          fImportMap.put(tns, ins);
          ins.addElement(schemaNamespace);
        } else if (!ins.contains(schemaNamespace)) {
          ins.addElement(schemaNamespace);
        }

        fSchemaGrammarDescription.reset();
        fSchemaGrammarDescription.setContextType(XSDDescription.CONTEXT_IMPORT);
        fSchemaGrammarDescription.setBaseSystemId(doc2SystemId(schemaRoot));
        fSchemaGrammarDescription.setLiteralSystemId(schemaHint);
        fSchemaGrammarDescription.setLocationHints(new String[]{schemaHint});
        fSchemaGrammarDescription.setTargetNamespace(schemaNamespace);

        // if a grammar with the same namespace and location exists (or being
        // built), ignore this one (don't traverse it).
        SchemaGrammar isg = findGrammar(fSchemaGrammarDescription, fNamespaceGrowth);
        if (isg != null) {
          if (fNamespaceGrowth) {
            try {
              if (isg.getDocumentLocations().contains(XMLEntityManager
                  .expandSystemId(schemaHint, fSchemaGrammarDescription.getBaseSystemId(),
                      false))) {
                continue;
              } else {
                importCollision = true;
              }
            } catch (MalformedURIException e) {
            }
          } else if (!fHonourAllSchemaLocations || isExistingGrammar(fSchemaGrammarDescription,
              false)) {
            continue;
          }
        }
        //if ((!fHonourAllSchemaLocations && findGrammar(fSchemaGrammarDescription) != null) || isExistingGrammar(fSchemaGrammarDescription))
        //    continue;

        // If "findGrammar" returns a grammar, then this is not the
        // the first time we see a location for a given namespace.
        // Don't consult the location pair hashtable in this case,
        // otherwise the location will be ignored because it'll get
        // resolved to the same location as the first hint.
        newSchemaRoot = resolveSchema(fSchemaGrammarDescription, false, child, isg == null);
      } else if ((localName.equals(SchemaSymbols.ELT_INCLUDE)) ||
          (localName.equals(SchemaSymbols.ELT_REDEFINE))) {
        // validation for redefine/include will be the same here; just
        // make sure TNS is right (don't care about redef contents
        // yet).
        Object[] includeAttrs = fAttributeChecker.checkAttributes(child, true, currSchemaInfo);
        schemaHint = (String) includeAttrs[XSAttributeChecker.ATTIDX_SCHEMALOCATION];
        // store the namespace decls of the redefine element
        if (localName.equals(SchemaSymbols.ELT_REDEFINE)) {
          if (fRedefine2NSSupport == null) {
            fRedefine2NSSupport = new HashMap();
          }
          fRedefine2NSSupport
              .put(child, new SchemaNamespaceSupport(currSchemaInfo.fNamespaceSupport));
        }

        // check annotations.  Must do this here to avoid having to
        // re-parse attributes later
        if (localName.equals(SchemaSymbols.ELT_INCLUDE)) {
          Element includeChild = DOMUtil.getFirstChildElement(child);
          if (includeChild != null) {
            String includeComponentType = DOMUtil.getLocalName(includeChild);
            if (includeComponentType.equals(SchemaSymbols.ELT_ANNOTATION)) {
              // promoting annotations to parent component
              sg.addAnnotation(
                  fElementTraverser
                      .traverseAnnotationDecl(includeChild, includeAttrs, true, currSchemaInfo));
            } else {
              reportSchemaError("s4s-elt-must-match.1",
                  new Object[]{localName, "annotation?", includeComponentType}, child);
            }
            if (DOMUtil.getNextSiblingElement(includeChild) != null) {
              reportSchemaError("s4s-elt-must-match.1", new Object[]{localName, "annotation?",
                  DOMUtil.getLocalName(DOMUtil.getNextSiblingElement(includeChild))}, child);
            }
          } else {
            String text = DOMUtil.getSyntheticAnnotation(child);
            if (text != null) {
              sg.addAnnotation(fElementTraverser
                  .traverseSyntheticAnnotation(child, text, includeAttrs, true, currSchemaInfo));
            }
          }
        } else {
          for (Element redefinedChild = DOMUtil.getFirstChildElement(child);
              redefinedChild != null;
              redefinedChild = DOMUtil.getNextSiblingElement(redefinedChild)) {
            String redefinedComponentType = DOMUtil.getLocalName(redefinedChild);
            if (redefinedComponentType.equals(SchemaSymbols.ELT_ANNOTATION)) {
              // promoting annotations to parent component
              sg.addAnnotation(
                  fElementTraverser
                      .traverseAnnotationDecl(redefinedChild, includeAttrs, true, currSchemaInfo));
              DOMUtil.setHidden(redefinedChild, fHiddenNodes);
            } else {
              String text = DOMUtil.getSyntheticAnnotation(child);
              if (text != null) {
                sg.addAnnotation(fElementTraverser
                    .traverseSyntheticAnnotation(child, text, includeAttrs, true, currSchemaInfo));
              }
            }
            // catch all other content errors later
          }
        }
        fAttributeChecker.returnAttrArray(includeAttrs, currSchemaInfo);
        // schemaLocation is required on <include> and <redefine>
        if (schemaHint == null) {
          reportSchemaError("s4s-att-must-appear", new Object[]{
                  "<include> or <redefine>", "schemaLocation"},
              child);
        }
        // pass the systemId of the current document as the base systemId
        boolean mustResolve = false;
        refType = XSDDescription.CONTEXT_INCLUDE;
        if (localName.equals(SchemaSymbols.ELT_REDEFINE)) {
          mustResolve = nonAnnotationContent(child);
          refType = XSDDescription.CONTEXT_REDEFINE;
        }
        fSchemaGrammarDescription.reset();
        fSchemaGrammarDescription.setContextType(refType);
        fSchemaGrammarDescription.setBaseSystemId(doc2SystemId(schemaRoot));
        fSchemaGrammarDescription.setLocationHints(new String[]{schemaHint});
        fSchemaGrammarDescription.setTargetNamespace(callerTNS);

        boolean alreadyTraversed = false;
        XMLInputSource schemaSource = resolveSchemaSource(fSchemaGrammarDescription, mustResolve,
            child, true);
        if (fNamespaceGrowth && refType == XSDDescription.CONTEXT_INCLUDE) {
          try {
            final String schemaId = XMLEntityManager
                .expandSystemId(schemaSource.getSystemId(), schemaSource.getBaseSystemId(), false);
            alreadyTraversed = sg.getDocumentLocations().contains(schemaId);
          } catch (MalformedURIException e) {

          }
        }

        if (!alreadyTraversed) {
          newSchemaRoot = resolveSchema(schemaSource, fSchemaGrammarDescription, mustResolve,
              child);
          schemaNamespace = currSchemaInfo.fTargetNamespace;
        } else {
          fLastSchemaWasDuplicate = true;
        }
      } else {
        // no more possibility of schema references in well-formed
        // schema...
        break;
      }

      // If the schema is duplicate, we needn't call constructTrees() again.
      // To handle mutual <include>s
      XSDocumentInfo newSchemaInfo = null;
      if (fLastSchemaWasDuplicate) {
        newSchemaInfo =
            newSchemaRoot == null ? null : (XSDocumentInfo) fDoc2XSDocumentMap.get(newSchemaRoot);
      } else {
        newSchemaInfo = constructTrees(newSchemaRoot, schemaHint, fSchemaGrammarDescription,
            importCollision);
      }

      if (localName.equals(SchemaSymbols.ELT_REDEFINE) &&
          newSchemaInfo != null) {
        // must record which schema we're redefining so that we can
        // rename the right things later!
        if (fRedefine2XSDMap == null) {
          fRedefine2XSDMap = new HashMap();
        }
        fRedefine2XSDMap.put(child, newSchemaInfo);
      }
      if (newSchemaRoot != null) {
        if (newSchemaInfo != null) {
          dependencies.addElement(newSchemaInfo);
        }
        newSchemaRoot = null;
      }
    }

    fDependencyMap.put(currSchemaInfo, dependencies);
    return currSchemaInfo;
  } // end constructTrees

  private boolean isExistingGrammar(XSDDescription desc, boolean ignoreConflict) {
    SchemaGrammar sg = fGrammarBucket.getGrammar(desc.getTargetNamespace());
    if (sg == null) {
      return findGrammar(desc, ignoreConflict) != null;
    } else if (sg.isImmutable()) {
      return true;
    } else {
      try {
        return sg.getDocumentLocations().contains(XMLEntityManager
            .expandSystemId(desc.getLiteralSystemId(), desc.getBaseSystemId(), false));
      } catch (MalformedURIException e) {
        return false;
      }
    }
  }

  /**
   * Namespace growth
   *
   * Go through the import list of a given grammar and for each imported
   * grammar, check to see if the grammar bucket has a newer version.
   * If a new instance is found, we update the import list with the
   * newer version.
   */
  private void updateImportListFor(SchemaGrammar grammar) {
    Vector importedGrammars = grammar.getImportedGrammars();
    if (importedGrammars != null) {
      for (int i = 0; i < importedGrammars.size(); i++) {
        SchemaGrammar isg1 = (SchemaGrammar) importedGrammars.elementAt(i);
        SchemaGrammar isg2 = fGrammarBucket.getGrammar(isg1.getTargetNamespace());
        if (isg2 != null && isg1 != isg2) {
          importedGrammars.set(i, isg2);
        }
      }
    }
  }

  /**
   * Namespace growth
   *
   * Go throuth the grammar bucket, and for each grammar in the bucket
   * check the import list. If there exists a grammar in import list
   * that has the same namespace as newGrammar, but a different instance,
   * then update the import list and replace the old grammar instance with
   * the new one
   */
  private void updateImportListWith(SchemaGrammar newGrammar) {
    SchemaGrammar[] schemaGrammars = fGrammarBucket.getGrammars();
    for (int i = 0; i < schemaGrammars.length; ++i) {
      SchemaGrammar sg = schemaGrammars[i];
      if (sg != newGrammar) {
        Vector importedGrammars = sg.getImportedGrammars();
        if (importedGrammars != null) {
          for (int j = 0; j < importedGrammars.size(); j++) {
            SchemaGrammar isg = (SchemaGrammar) importedGrammars.elementAt(j);
            if (null2EmptyString(isg.getTargetNamespace())
                .equals(null2EmptyString(newGrammar.getTargetNamespace()))) {
              if (isg != newGrammar) {
                importedGrammars.set(j, newGrammar);
              }
              break;
            }
          }
        }
      }
    }
  }

  // This method builds registries for all globally-referenceable
  // names.  A registry will be built for each symbol space defined
  // by the spec.  It is also this method's job to rename redefined
  // components, and to record which components redefine others (so
  // that implicit redefinitions of groups and attributeGroups can be handled).
  protected void buildGlobalNameRegistries() {

    registryEmpty = false;
    // Starting with fRoot, we examine each child of the schema
    // element.  Skipping all imports and includes, we record the names
    // of all other global components (and children of <redefine>).  We
    // also put <redefine> names in a registry that we look through in
    // case something needs renaming.  Once we're done with a schema we
    // set its Document node to hidden so that we don't try to traverse
    // it again; then we look to its Dependency map entry.  We keep a
    // stack of schemas that we haven't yet finished processing; this
    // is a depth-first traversal.

    Stack schemasToProcess = new Stack();
    schemasToProcess.push(fRoot);

    while (!schemasToProcess.empty()) {
      XSDocumentInfo currSchemaDoc =
          (XSDocumentInfo) schemasToProcess.pop();
      Element currDoc = currSchemaDoc.fSchemaElement;
      if (DOMUtil.isHidden(currDoc, fHiddenNodes)) {
        // must have processed this already!
        continue;
      }

      Element currRoot = currDoc;
      // process this schema's global decls
      boolean dependenciesCanOccur = true;
      for (Element globalComp =
          DOMUtil.getFirstChildElement(currRoot);
          globalComp != null;
          globalComp = DOMUtil.getNextSiblingElement(globalComp)) {
        // this loop makes sure the <schema> element ordering is
        // also valid.
        if (DOMUtil.getLocalName(globalComp).equals(SchemaSymbols.ELT_ANNOTATION)) {
          //skip it; traverse it later
          continue;
        } else if (DOMUtil.getLocalName(globalComp).equals(SchemaSymbols.ELT_INCLUDE) ||
            DOMUtil.getLocalName(globalComp).equals(SchemaSymbols.ELT_IMPORT)) {
          if (!dependenciesCanOccur) {
            reportSchemaError("s4s-elt-invalid-content.3",
                new Object[]{DOMUtil.getLocalName(globalComp)}, globalComp);
          }
          DOMUtil.setHidden(globalComp, fHiddenNodes);
        } else if (DOMUtil.getLocalName(globalComp).equals(SchemaSymbols.ELT_REDEFINE)) {
          if (!dependenciesCanOccur) {
            reportSchemaError("s4s-elt-invalid-content.3",
                new Object[]{DOMUtil.getLocalName(globalComp)}, globalComp);
          }
          for (Element redefineComp = DOMUtil.getFirstChildElement(globalComp);
              redefineComp != null;
              redefineComp = DOMUtil.getNextSiblingElement(redefineComp)) {
            String lName = DOMUtil.getAttrValue(redefineComp, SchemaSymbols.ATT_NAME);
            if (lName.length() == 0) // an error we'll catch later
            {
              continue;
            }
            String qName = currSchemaDoc.fTargetNamespace == null ?
                "," + lName :
                currSchemaDoc.fTargetNamespace + "," + lName;
            String componentType = DOMUtil.getLocalName(redefineComp);
            if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
              checkForDuplicateNames(qName, ATTRIBUTEGROUP_TYPE, fUnparsedAttributeGroupRegistry,
                  fUnparsedAttributeGroupRegistrySub, redefineComp, currSchemaDoc);
              // the check will have changed our name;
              String targetLName =
                  DOMUtil.getAttrValue(redefineComp, SchemaSymbols.ATT_NAME) + REDEF_IDENTIFIER;
              // and all we need to do is error-check+rename our kkids:
              renameRedefiningComponents(currSchemaDoc, redefineComp,
                  SchemaSymbols.ELT_ATTRIBUTEGROUP,
                  lName, targetLName);
            } else if ((componentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) ||
                (componentType.equals(SchemaSymbols.ELT_SIMPLETYPE))) {
              checkForDuplicateNames(qName, TYPEDECL_TYPE, fUnparsedTypeRegistry,
                  fUnparsedTypeRegistrySub, redefineComp, currSchemaDoc);
              // the check will have changed our name;
              String targetLName =
                  DOMUtil.getAttrValue(redefineComp, SchemaSymbols.ATT_NAME) + REDEF_IDENTIFIER;
              // and all we need to do is error-check+rename our kkids:
              if (componentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) {
                renameRedefiningComponents(currSchemaDoc, redefineComp,
                    SchemaSymbols.ELT_COMPLEXTYPE,
                    lName, targetLName);
              } else { // must be simpleType
                renameRedefiningComponents(currSchemaDoc, redefineComp,
                    SchemaSymbols.ELT_SIMPLETYPE,
                    lName, targetLName);
              }
            } else if (componentType.equals(SchemaSymbols.ELT_GROUP)) {
              checkForDuplicateNames(qName, GROUP_TYPE, fUnparsedGroupRegistry,
                  fUnparsedGroupRegistrySub, redefineComp, currSchemaDoc);
              // the check will have changed our name;
              String targetLName =
                  DOMUtil.getAttrValue(redefineComp, SchemaSymbols.ATT_NAME) + REDEF_IDENTIFIER;
              // and all we need to do is error-check+rename our kids:
              renameRedefiningComponents(currSchemaDoc, redefineComp, SchemaSymbols.ELT_GROUP,
                  lName, targetLName);
            }
          } // end march through <redefine> children
          // and now set as traversed
          //DOMUtil.setHidden(globalComp);
        } else {
          dependenciesCanOccur = false;
          String lName = DOMUtil.getAttrValue(globalComp, SchemaSymbols.ATT_NAME);
          if (lName.length() == 0) // an error we'll catch later
          {
            continue;
          }
          String qName = currSchemaDoc.fTargetNamespace == null ?
              "," + lName :
              currSchemaDoc.fTargetNamespace + "," + lName;
          String componentType = DOMUtil.getLocalName(globalComp);

          if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTE)) {
            checkForDuplicateNames(qName, ATTRIBUTE_TYPE, fUnparsedAttributeRegistry,
                fUnparsedAttributeRegistrySub, globalComp, currSchemaDoc);
          } else if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
            checkForDuplicateNames(qName, ATTRIBUTEGROUP_TYPE, fUnparsedAttributeGroupRegistry,
                fUnparsedAttributeGroupRegistrySub, globalComp, currSchemaDoc);
          } else if ((componentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) ||
              (componentType.equals(SchemaSymbols.ELT_SIMPLETYPE))) {
            checkForDuplicateNames(qName, TYPEDECL_TYPE, fUnparsedTypeRegistry,
                fUnparsedTypeRegistrySub, globalComp, currSchemaDoc);
          } else if (componentType.equals(SchemaSymbols.ELT_ELEMENT)) {
            checkForDuplicateNames(qName, ELEMENT_TYPE, fUnparsedElementRegistry,
                fUnparsedElementRegistrySub, globalComp, currSchemaDoc);
          } else if (componentType.equals(SchemaSymbols.ELT_GROUP)) {
            checkForDuplicateNames(qName, GROUP_TYPE, fUnparsedGroupRegistry,
                fUnparsedGroupRegistrySub, globalComp, currSchemaDoc);
          } else if (componentType.equals(SchemaSymbols.ELT_NOTATION)) {
            checkForDuplicateNames(qName, NOTATION_TYPE, fUnparsedNotationRegistry,
                fUnparsedNotationRegistrySub, globalComp, currSchemaDoc);
          }
        }
      } // end for

      // now we're done with this one!
      DOMUtil.setHidden(currDoc, fHiddenNodes);
      // now add the schemas this guy depends on
      Vector currSchemaDepends = (Vector) fDependencyMap.get(currSchemaDoc);
      for (int i = 0; i < currSchemaDepends.size(); i++) {
        schemasToProcess.push(currSchemaDepends.elementAt(i));
      }
    } // while

  } // end buildGlobalNameRegistries

  // Beginning at the first schema processing was requested for
  // (fRoot), this method
  // examines each child (global schema information item) of each
  // schema document (and of each <redefine> element)
  // corresponding to an XSDocumentInfo object.  If the
  // readOnly field on that node has not been set, it calls an
  // appropriate traverser to traverse it.  Once all global decls in
  // an XSDocumentInfo object have been traversed, it marks that object
  // as traversed (or hidden) in order to avoid infinite loops.  It completes
  // when it has visited all XSDocumentInfo objects in the
  // DependencyMap and marked them as traversed.
  protected void traverseSchemas(ArrayList annotationInfo) {
    // the process here is very similar to that in
    // buildGlobalRegistries, except we can't set our schemas as
    // hidden for a second time; so make them all visible again
    // first!
    setSchemasVisible(fRoot);
    Stack schemasToProcess = new Stack();
    schemasToProcess.push(fRoot);
    while (!schemasToProcess.empty()) {
      XSDocumentInfo currSchemaDoc =
          (XSDocumentInfo) schemasToProcess.pop();
      Element currDoc = currSchemaDoc.fSchemaElement;

      SchemaGrammar currSG = fGrammarBucket.getGrammar(currSchemaDoc.fTargetNamespace);

      if (DOMUtil.isHidden(currDoc, fHiddenNodes)) {
        // must have processed this already!
        continue;
      }
      Element currRoot = currDoc;
      boolean sawAnnotation = false;
      // traverse this schema's global decls
      for (Element globalComp =
          DOMUtil.getFirstVisibleChildElement(currRoot, fHiddenNodes);
          globalComp != null;
          globalComp = DOMUtil.getNextVisibleSiblingElement(globalComp, fHiddenNodes)) {
        DOMUtil.setHidden(globalComp, fHiddenNodes);
        String componentType = DOMUtil.getLocalName(globalComp);
        // includes and imports will not show up here!
        if (DOMUtil.getLocalName(globalComp).equals(SchemaSymbols.ELT_REDEFINE)) {
          // use the namespace decls for the redefine, instead of for the parent <schema>
          currSchemaDoc.backupNSSupport(
              (fRedefine2NSSupport != null) ? (SchemaNamespaceSupport) fRedefine2NSSupport
                  .get(globalComp) : null);
          for (
              Element redefinedComp = DOMUtil.getFirstVisibleChildElement(globalComp, fHiddenNodes);
              redefinedComp != null;
              redefinedComp = DOMUtil.getNextVisibleSiblingElement(redefinedComp, fHiddenNodes)) {
            String redefinedComponentType = DOMUtil.getLocalName(redefinedComp);
            DOMUtil.setHidden(redefinedComp, fHiddenNodes);
            if (redefinedComponentType.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
              fAttributeGroupTraverser.traverseGlobal(redefinedComp, currSchemaDoc, currSG);
            } else if (redefinedComponentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) {
              fComplexTypeTraverser.traverseGlobal(redefinedComp, currSchemaDoc, currSG);
            } else if (redefinedComponentType.equals(SchemaSymbols.ELT_GROUP)) {
              fGroupTraverser.traverseGlobal(redefinedComp, currSchemaDoc, currSG);
            } else if (redefinedComponentType.equals(SchemaSymbols.ELT_SIMPLETYPE)) {
              fSimpleTypeTraverser.traverseGlobal(redefinedComp, currSchemaDoc, currSG);
            }
            // annotations will have been processed already; this is now
            // unnecessary
            //else if (redefinedComponentType.equals(SchemaSymbols.ELT_ANNOTATION)) {
            //    fElementTraverser.traverseAnnotationDecl(redefinedComp, null, true, currSchemaDoc);
            //}
            else {
              reportSchemaError("s4s-elt-must-match.1",
                  new Object[]{DOMUtil.getLocalName(globalComp),
                      "(annotation | (simpleType | complexType | group | attributeGroup))*",
                      redefinedComponentType}, redefinedComp);
            }
          } // end march through <redefine> children
          currSchemaDoc.restoreNSSupport();
        } else if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTE)) {
          fAttributeTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
        } else if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
          fAttributeGroupTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
        } else if (componentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) {
          fComplexTypeTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
        } else if (componentType.equals(SchemaSymbols.ELT_ELEMENT)) {
          fElementTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
        } else if (componentType.equals(SchemaSymbols.ELT_GROUP)) {
          fGroupTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
        } else if (componentType.equals(SchemaSymbols.ELT_NOTATION)) {
          fNotationTraverser.traverse(globalComp, currSchemaDoc, currSG);
        } else if (componentType.equals(SchemaSymbols.ELT_SIMPLETYPE)) {
          fSimpleTypeTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
        } else if (componentType.equals(SchemaSymbols.ELT_ANNOTATION)) {
          currSG.addAnnotation(fElementTraverser
              .traverseAnnotationDecl(globalComp, currSchemaDoc.getSchemaAttrs(), true,
                  currSchemaDoc));
          sawAnnotation = true;
        } else {
          reportSchemaError("s4s-elt-invalid-content.1",
              new Object[]{SchemaSymbols.ELT_SCHEMA, DOMUtil.getLocalName(globalComp)}, globalComp);
        }
      } // end for

      if (!sawAnnotation) {
        String text = DOMUtil.getSyntheticAnnotation(currRoot);
        if (text != null) {
          currSG.addAnnotation(fElementTraverser
              .traverseSyntheticAnnotation(currRoot, text, currSchemaDoc.getSchemaAttrs(), true,
                  currSchemaDoc));
        }
      }

      /** Collect annotation information for validation. **/
      if (annotationInfo != null) {
        XSAnnotationInfo info = currSchemaDoc.getAnnotations();
        /** Only add annotations to the list if there were any in this document. **/
        if (info != null) {
          annotationInfo.add(doc2SystemId(currDoc));
          annotationInfo.add(info);
        }
      }
      // now we're done with this one!
      currSchemaDoc.returnSchemaAttrs();
      DOMUtil.setHidden(currDoc, fHiddenNodes);

      // now add the schemas this guy depends on
      Vector currSchemaDepends = (Vector) fDependencyMap.get(currSchemaDoc);
      for (int i = 0; i < currSchemaDepends.size(); i++) {
        schemasToProcess.push(currSchemaDepends.elementAt(i));
      }
    } // while
  } // end traverseSchemas

  // store whether we have reported an error about that no grammar
  // is found for the given namespace uri
  private Vector fReportedTNS = null;

  // check whether we need to report an error against the given uri.
  // if we have reported an error, then we don't need to report again;
  // otherwise we reported the error, and remember this fact.
  private final boolean needReportTNSError(String uri) {
    if (fReportedTNS == null) {
      fReportedTNS = new Vector();
    } else if (fReportedTNS.contains(uri)) {
      return false;
    }
    fReportedTNS.addElement(uri);
    return true;
  }

  private static final String[] COMP_TYPE = {
      null,               // index 0
      "attribute declaration",
      "attribute group",
      "element declaration",
      "group",
      "identity constraint",
      "notation",
      "type definition",
  };

  private static final String[] CIRCULAR_CODES = {
      "Internal-Error",
      "Internal-Error",
      "src-attribute_group.3",
      "e-props-correct.6",
      "mg-props-correct.2",
      "Internal-Error",
      "Internal-Error",
      "st-props-correct.2",       //or ct-props-correct.3
  };

  // add a global attribute decl from a current schema load (only if no existing decl is found)
  void addGlobalAttributeDecl(XSAttributeDecl decl) {
    final String namespace = decl.getNamespace();
    final String declKey = (namespace == null || namespace.length() == 0)
        ? "," + decl.getName() : namespace + "," + decl.getName();

    if (fGlobalAttrDecls.get(declKey) == null) {
      fGlobalAttrDecls.put(declKey, decl);
    }
  }

  // add a global attribute group decl from a current schema load (only if no existing decl is found)
  void addGlobalAttributeGroupDecl(XSAttributeGroupDecl decl) {
    final String namespace = decl.getNamespace();
    final String declKey = (namespace == null || namespace.length() == 0)
        ? "," + decl.getName() : namespace + "," + decl.getName();

    if (fGlobalAttrGrpDecls.get(declKey) == null) {
      fGlobalAttrGrpDecls.put(declKey, decl);
    }
  }

  // add a global element decl from a current schema load (only if no existing decl is found)
  void addGlobalElementDecl(XSElementDecl decl) {
    final String namespace = decl.getNamespace();
    final String declKey = (namespace == null || namespace.length() == 0)
        ? "," + decl.getName() : namespace + "," + decl.getName();

    if (fGlobalElemDecls.get(declKey) == null) {
      fGlobalElemDecls.put(declKey, decl);
    }
  }

  // add a global group decl from a current schema load (only if no existing decl is found)
  void addGlobalGroupDecl(XSGroupDecl decl) {
    final String namespace = decl.getNamespace();
    final String declKey = (namespace == null || namespace.length() == 0)
        ? "," + decl.getName() : namespace + "," + decl.getName();

    if (fGlobalGroupDecls.get(declKey) == null) {
      fGlobalGroupDecls.put(declKey, decl);
    }
  }

  // add a global notation decl from a current schema load (only if no existing decl is found)
  void addGlobalNotationDecl(XSNotationDecl decl) {
    final String namespace = decl.getNamespace();
    final String declKey = (namespace == null || namespace.length() == 0)
        ? "," + decl.getName() : namespace + "," + decl.getName();

    if (fGlobalNotationDecls.get(declKey) == null) {
      fGlobalNotationDecls.put(declKey, decl);
    }
  }

  // add a global type decl from a current schema load (only if no existing decl is found)
  void addGlobalTypeDecl(XSTypeDefinition decl) {
    final String namespace = decl.getNamespace();
    final String declKey = (namespace == null || namespace.length() == 0)
        ? "," + decl.getName() : namespace + "," + decl.getName();

    if (fGlobalTypeDecls.get(declKey) == null) {
      fGlobalTypeDecls.put(declKey, decl);
    }
  }

  // add a identity constraint decl from a current schema load (only if no existing decl is found)
  void addIDConstraintDecl(IdentityConstraint decl) {
    final String namespace = decl.getNamespace();
    final String declKey = (namespace == null || namespace.length() == 0)
        ? "," + decl.getIdentityConstraintName()
        : namespace + "," + decl.getIdentityConstraintName();

    if (fGlobalIDConstraintDecls.get(declKey) == null) {
      fGlobalIDConstraintDecls.put(declKey, decl);
    }
  }

  private XSAttributeDecl getGlobalAttributeDecl(String declKey) {
    return (XSAttributeDecl) fGlobalAttrDecls.get(declKey);
  }

  private XSAttributeGroupDecl getGlobalAttributeGroupDecl(String declKey) {
    return (XSAttributeGroupDecl) fGlobalAttrGrpDecls.get(declKey);
  }

  private XSElementDecl getGlobalElementDecl(String declKey) {
    return (XSElementDecl) fGlobalElemDecls.get(declKey);
  }

  private XSGroupDecl getGlobalGroupDecl(String declKey) {
    return (XSGroupDecl) fGlobalGroupDecls.get(declKey);
  }

  private XSNotationDecl getGlobalNotationDecl(String declKey) {
    return (XSNotationDecl) fGlobalNotationDecls.get(declKey);
  }

  private XSTypeDefinition getGlobalTypeDecl(String declKey) {
    return (XSTypeDefinition) fGlobalTypeDecls.get(declKey);
  }

  private IdentityConstraint getIDConstraintDecl(String declKey) {
    return (IdentityConstraint) fGlobalIDConstraintDecls.get(declKey);
  }

  // since it is forbidden for traversers to talk to each other
  // directly (except wen a traverser encounters a local declaration),
  // this provides a generic means for a traverser to call
  // for the traversal of some declaration.  An XSDocumentInfo is
  // required because the XSDocumentInfo that the traverser is traversing
  // may bear no relation to the one the handler is operating on.
  // This method will:
  // 1.  See if a global definition matching declToTraverse exists;
  // 2. if so, determine if there is a path from currSchema to the
  // schema document where declToTraverse lives (i.e., do a lookup
  // in DependencyMap);
  // 3. depending on declType (which will be relevant to step 1 as
  // well), call the appropriate traverser with the appropriate
  // XSDocumentInfo object.
  // This method returns whatever the traverser it called returned;
  // this will be an Object of some kind
  // that lives in the Grammar.
  protected Object getGlobalDecl(XSDocumentInfo currSchema,
      int declType,
      QName declToTraverse,
      Element elmNode) {

    if (DEBUG_NODE_POOL) {
      System.out.println("TRAVERSE_GL: " + declToTraverse.toString());
    }

    // from the schema spec, all built-in types are present in all schemas,
    // so if the requested component is a type, and could be found in the
    // default schema grammar, we should return that type.
    // otherwise (since we would support user-defined schema grammar) we'll
    // use the normal way to get the decl
    if (declToTraverse.uri != null &&
        declToTraverse.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA) {
      if (declType == TYPEDECL_TYPE) {
        Object retObj = SchemaGrammar.SG_SchemaNS.getGlobalTypeDecl(declToTraverse.localpart);
        if (retObj != null) {
          return retObj;
        }
      }
    }

    // now check whether this document can access the requsted namespace
    if (!currSchema.isAllowedNS(declToTraverse.uri)) {
      // cannot get to this schema from the one containing the requesting decl
      if (currSchema.needReportTNSError(declToTraverse.uri)) {
        String code = declToTraverse.uri == null ? "src-resolve.4.1" : "src-resolve.4.2";
        reportSchemaError(code,
            new Object[]{fDoc2SystemId.get(currSchema.fSchemaElement), declToTraverse.uri,
                declToTraverse.rawname}, elmNode);
      }
      // Recover and continue to look for the component.
      // return null;
    }

    // check whether there is grammar for the requested namespace
    SchemaGrammar sGrammar = fGrammarBucket.getGrammar(declToTraverse.uri);
    if (sGrammar == null) {
      if (needReportTNSError(declToTraverse.uri)) {
        reportSchemaError("src-resolve", new Object[]{declToTraverse.rawname, COMP_TYPE[declType]},
            elmNode);
      }
      return null;
    }

    // if there is such grammar, check whether the requested component is in the grammar
    Object retObj = getGlobalDeclFromGrammar(sGrammar, declType, declToTraverse.localpart);
    String declKey = declToTraverse.uri == null ? "," + declToTraverse.localpart :
        declToTraverse.uri + "," + declToTraverse.localpart;

    // if the component is parsed, return it
    if (!fTolerateDuplicates) {
      if (retObj != null) {
        return retObj;
      }
    } else {
      Object retObj2 = getGlobalDecl(declKey, declType);
      if (retObj2 != null) {
        return retObj2;
      }
    }

    XSDocumentInfo schemaWithDecl = null;
    Element decl = null;
    XSDocumentInfo declDoc = null;

    // the component is not parsed, try to find a DOM element for it
    switch (declType) {
      case ATTRIBUTE_TYPE:
        decl = getElementFromMap(fUnparsedAttributeRegistry, declKey);
        declDoc = getDocInfoFromMap(fUnparsedAttributeRegistrySub, declKey);
        break;
      case ATTRIBUTEGROUP_TYPE:
        decl = getElementFromMap(fUnparsedAttributeGroupRegistry, declKey);
        declDoc = getDocInfoFromMap(fUnparsedAttributeGroupRegistrySub, declKey);
        break;
      case ELEMENT_TYPE:
        decl = getElementFromMap(fUnparsedElementRegistry, declKey);
        declDoc = getDocInfoFromMap(fUnparsedElementRegistrySub, declKey);
        break;
      case GROUP_TYPE:
        decl = getElementFromMap(fUnparsedGroupRegistry, declKey);
        declDoc = getDocInfoFromMap(fUnparsedGroupRegistrySub, declKey);
        break;
      case IDENTITYCONSTRAINT_TYPE:
        decl = getElementFromMap(fUnparsedIdentityConstraintRegistry, declKey);
        declDoc = getDocInfoFromMap(fUnparsedIdentityConstraintRegistrySub, declKey);
        break;
      case NOTATION_TYPE:
        decl = getElementFromMap(fUnparsedNotationRegistry, declKey);
        declDoc = getDocInfoFromMap(fUnparsedNotationRegistrySub, declKey);
        break;
      case TYPEDECL_TYPE:
        decl = getElementFromMap(fUnparsedTypeRegistry, declKey);
        declDoc = getDocInfoFromMap(fUnparsedTypeRegistrySub, declKey);
        break;
      default:
        reportSchemaError("Internal-Error", new Object[]{
            "XSDHandler asked to locate component of type " + declType
                + "; it does not recognize this type!"}, elmNode);
    }

    // no DOM element found, so the component can't be located
    if (decl == null) {
      if (retObj == null) {
        reportSchemaError("src-resolve", new Object[]{declToTraverse.rawname, COMP_TYPE[declType]},
            elmNode);
      }
      return retObj;
    }

    // get the schema doc containing the component to be parsed
    // it should always return non-null value, but since null-checking
    // comes for free, let's be safe and check again
    schemaWithDecl = findXSDocumentForDecl(currSchema, decl, declDoc);
    if (schemaWithDecl == null) {
      // cannot get to this schema from the one containing the requesting decl
      if (retObj == null) {
        String code = declToTraverse.uri == null ? "src-resolve.4.1" : "src-resolve.4.2";
        reportSchemaError(code,
            new Object[]{fDoc2SystemId.get(currSchema.fSchemaElement), declToTraverse.uri,
                declToTraverse.rawname}, elmNode);
      }
      return retObj;
    }

    // a component is hidden, meaning either it's traversed, or being traversed.
    // but we didn't find it in the grammar, so it's the latter case, and
    // a circular reference. error!
    if (DOMUtil.isHidden(decl, fHiddenNodes)) {
      if (retObj == null) {
        String code = CIRCULAR_CODES[declType];
        if (declType == TYPEDECL_TYPE) {
          if (SchemaSymbols.ELT_COMPLEXTYPE.equals(DOMUtil.getLocalName(decl))) {
            code = "ct-props-correct.3";
          }
        }
        // decl must not be null if we're here...
        reportSchemaError(code,
            new Object[]{declToTraverse.prefix + ":" + declToTraverse.localpart}, elmNode);
      }
      return retObj;
    }

    return traverseGlobalDecl(declType, decl, schemaWithDecl, sGrammar);
  } // getGlobalDecl(XSDocumentInfo, int, QName):  Object

  // If we are tolerating duplicate declarations and allowing namespace growth
  // use the declaration from the current schema load (if it exists)
  protected Object getGlobalDecl(String declKey, int declType) {
    Object retObj = null;

    switch (declType) {
      case ATTRIBUTE_TYPE:
        retObj = getGlobalAttributeDecl(declKey);
        break;
      case ATTRIBUTEGROUP_TYPE:
        retObj = getGlobalAttributeGroupDecl(declKey);
        break;
      case ELEMENT_TYPE:
        retObj = getGlobalElementDecl(declKey);
        break;
      case GROUP_TYPE:
        retObj = getGlobalGroupDecl(declKey);
        break;
      case IDENTITYCONSTRAINT_TYPE:
        retObj = getIDConstraintDecl(declKey);
        break;
      case NOTATION_TYPE:
        retObj = getGlobalNotationDecl(declKey);
        break;
      case TYPEDECL_TYPE:
        retObj = getGlobalTypeDecl(declKey);
        break;
    }

    return retObj;
  }

  protected Object getGlobalDeclFromGrammar(SchemaGrammar sGrammar, int declType,
      String localpart) {
    Object retObj = null;

    switch (declType) {
      case ATTRIBUTE_TYPE:
        retObj = sGrammar.getGlobalAttributeDecl(localpart);
        break;
      case ATTRIBUTEGROUP_TYPE:
        retObj = sGrammar.getGlobalAttributeGroupDecl(localpart);
        break;
      case ELEMENT_TYPE:
        retObj = sGrammar.getGlobalElementDecl(localpart);
        break;
      case GROUP_TYPE:
        retObj = sGrammar.getGlobalGroupDecl(localpart);
        break;
      case IDENTITYCONSTRAINT_TYPE:
        retObj = sGrammar.getIDConstraintDecl(localpart);
        break;
      case NOTATION_TYPE:
        retObj = sGrammar.getGlobalNotationDecl(localpart);
        break;
      case TYPEDECL_TYPE:
        retObj = sGrammar.getGlobalTypeDecl(localpart);
        break;
    }

    return retObj;
  }

  protected Object getGlobalDeclFromGrammar(SchemaGrammar sGrammar, int declType, String localpart,
      String schemaLoc) {
    Object retObj = null;

    switch (declType) {
      case ATTRIBUTE_TYPE:
        retObj = sGrammar.getGlobalAttributeDecl(localpart, schemaLoc);
        break;
      case ATTRIBUTEGROUP_TYPE:
        retObj = sGrammar.getGlobalAttributeGroupDecl(localpart, schemaLoc);
        break;
      case ELEMENT_TYPE:
        retObj = sGrammar.getGlobalElementDecl(localpart, schemaLoc);
        break;
      case GROUP_TYPE:
        retObj = sGrammar.getGlobalGroupDecl(localpart, schemaLoc);
        break;
      case IDENTITYCONSTRAINT_TYPE:
        retObj = sGrammar.getIDConstraintDecl(localpart, schemaLoc);
        break;
      case NOTATION_TYPE:
        retObj = sGrammar.getGlobalNotationDecl(localpart, schemaLoc);
        break;
      case TYPEDECL_TYPE:
        retObj = sGrammar.getGlobalTypeDecl(localpart, schemaLoc);
        break;
    }

    return retObj;
  }

  protected Object traverseGlobalDecl(int declType, Element decl, XSDocumentInfo schemaDoc,
      SchemaGrammar grammar) {
    Object retObj = null;

    DOMUtil.setHidden(decl, fHiddenNodes);
    SchemaNamespaceSupport nsSupport = null;
    // if the parent is <redefine> use the namespace delcs for it.
    Element parent = DOMUtil.getParent(decl);
    if (DOMUtil.getLocalName(parent).equals(SchemaSymbols.ELT_REDEFINE)) {
      nsSupport =
          (fRedefine2NSSupport != null) ? (SchemaNamespaceSupport) fRedefine2NSSupport.get(parent)
              : null;
    }
    // back up the current SchemaNamespaceSupport, because we need to provide
    // a fresh one to the traverseGlobal methods.
    schemaDoc.backupNSSupport(nsSupport);

    // traverse the referenced global component
    switch (declType) {
      case TYPEDECL_TYPE:
        if (DOMUtil.getLocalName(decl).equals(SchemaSymbols.ELT_COMPLEXTYPE)) {
          retObj = fComplexTypeTraverser.traverseGlobal(decl, schemaDoc, grammar);
        } else {
          retObj = fSimpleTypeTraverser.traverseGlobal(decl, schemaDoc, grammar);
        }
        break;
      case ATTRIBUTE_TYPE:
        retObj = fAttributeTraverser.traverseGlobal(decl, schemaDoc, grammar);
        break;
      case ELEMENT_TYPE:
        retObj = fElementTraverser.traverseGlobal(decl, schemaDoc, grammar);
        break;
      case ATTRIBUTEGROUP_TYPE:
        retObj = fAttributeGroupTraverser.traverseGlobal(decl, schemaDoc, grammar);
        break;
      case GROUP_TYPE:
        retObj = fGroupTraverser.traverseGlobal(decl, schemaDoc, grammar);
        break;
      case NOTATION_TYPE:
        retObj = fNotationTraverser.traverse(decl, schemaDoc, grammar);
        break;
      case IDENTITYCONSTRAINT_TYPE:
        // identity constraints should have been parsed already...
        // we should never get here
        break;
    }

    // restore the previous SchemaNamespaceSupport, so that the caller can get
    // proper namespace binding.
    schemaDoc.restoreNSSupport();

    return retObj;
  }

  public String schemaDocument2SystemId(XSDocumentInfo schemaDoc) {
    return (String) fDoc2SystemId.get(schemaDoc.fSchemaElement);
  }

  // This method determines whether there is a group
  // (attributeGroup) which the given one has redefined by
  // restriction.  If so, it returns it; else it returns null.
  // @param type:  whether what's been redefined is an
  // attributeGroup or a group;
  // @param name:  the QName of the component doing the redefining.
  // @param currSchema:  schema doc in which the redefining component lives.
  // @return:  Object representing decl redefined if present, null
  // otherwise.
  Object getGrpOrAttrGrpRedefinedByRestriction(int type, QName name, XSDocumentInfo currSchema,
      Element elmNode) {
    String realName = name.uri != null ? name.uri + "," + name.localpart :
        "," + name.localpart;
    String nameToFind = null;
    switch (type) {
      case ATTRIBUTEGROUP_TYPE:
        nameToFind = (String) fRedefinedRestrictedAttributeGroupRegistry.get(realName);
        break;
      case GROUP_TYPE:
        nameToFind = (String) fRedefinedRestrictedGroupRegistry.get(realName);
        break;
      default:
        return null;
    }
    if (nameToFind == null) {
      return null;
    }
    int commaPos = nameToFind.indexOf(",");
    QName qNameToFind = new QName(XMLSymbols.EMPTY_STRING, nameToFind.substring(commaPos + 1),
        nameToFind.substring(commaPos), (commaPos == 0) ? null : nameToFind.substring(0, commaPos));
    Object retObj = getGlobalDecl(currSchema, type, qNameToFind, elmNode);
    if (retObj == null) {
      switch (type) {
        case ATTRIBUTEGROUP_TYPE:
          reportSchemaError("src-redefine.7.2.1", new Object[]{name.localpart}, elmNode);
          break;
        case GROUP_TYPE:
          reportSchemaError("src-redefine.6.2.1", new Object[]{name.localpart}, elmNode);
          break;
      }
      return null;
    }
    return retObj;
  } // getGrpOrAttrGrpRedefinedByRestriction(int, QName, XSDocumentInfo):  Object

  // Since ID constraints can occur in local elements, unless we
  // wish to completely traverse all our DOM trees looking for ID
  // constraints while we're building our global name registries,
  // which seems terribly inefficient, we need to resolve keyrefs
  // after all parsing is complete.  This we can simply do by running through
  // fIdentityConstraintRegistry and calling traverseKeyRef on all
  // of the KeyRef nodes.  This unfortunately removes this knowledge
  // from the elementTraverser class (which must ignore keyrefs),
  // but there seems to be no efficient way around this...
  protected void resolveKeyRefs() {
    for (int i = 0; i < fKeyrefStackPos; i++) {
      XSDocumentInfo keyrefSchemaDoc = fKeyrefsMapXSDocumentInfo[i];
      keyrefSchemaDoc.fNamespaceSupport.makeGlobal();
      keyrefSchemaDoc.fNamespaceSupport.setEffectiveContext(fKeyrefNamespaceContext[i]);
      SchemaGrammar keyrefGrammar = fGrammarBucket.getGrammar(keyrefSchemaDoc.fTargetNamespace);
      // need to set <keyref> to hidden before traversing it,
      // because it has global scope
      DOMUtil.setHidden(fKeyrefs[i], fHiddenNodes);
      fKeyrefTraverser.traverse(fKeyrefs[i], fKeyrefElems[i], keyrefSchemaDoc, keyrefGrammar);
    }
  } // end resolveKeyRefs

  // an accessor method.  Just makes sure callers
  // who want the Identity constraint registry vaguely know what they're about.
  protected Map getIDRegistry() {
    return fUnparsedIdentityConstraintRegistry;
  }

  // an accessor method.
  protected Map getIDRegistry_sub() {
    return fUnparsedIdentityConstraintRegistrySub;
  }


  // This method squirrels away <keyref> declarations--along with the element
  // decls and namespace bindings they might find handy.
  protected void storeKeyRef(Element keyrefToStore, XSDocumentInfo schemaDoc,
      XSElementDecl currElemDecl) {
    String keyrefName = DOMUtil.getAttrValue(keyrefToStore, SchemaSymbols.ATT_NAME);
    if (keyrefName.length() != 0) {
      String keyrefQName = schemaDoc.fTargetNamespace == null ?
          "," + keyrefName : schemaDoc.fTargetNamespace + "," + keyrefName;
      checkForDuplicateNames(keyrefQName, IDENTITYCONSTRAINT_TYPE,
          fUnparsedIdentityConstraintRegistry, fUnparsedIdentityConstraintRegistrySub,
          keyrefToStore, schemaDoc);
    }
    // now set up all the registries we'll need...

    // check array sizes
    if (fKeyrefStackPos == fKeyrefs.length) {
      Element[] elemArray = new Element[fKeyrefStackPos + INC_KEYREF_STACK_AMOUNT];
      System.arraycopy(fKeyrefs, 0, elemArray, 0, fKeyrefStackPos);
      fKeyrefs = elemArray;
      XSElementDecl[] declArray = new XSElementDecl[fKeyrefStackPos + INC_KEYREF_STACK_AMOUNT];
      System.arraycopy(fKeyrefElems, 0, declArray, 0, fKeyrefStackPos);
      fKeyrefElems = declArray;
      String[][] stringArray = new String[fKeyrefStackPos + INC_KEYREF_STACK_AMOUNT][];
      System.arraycopy(fKeyrefNamespaceContext, 0, stringArray, 0, fKeyrefStackPos);
      fKeyrefNamespaceContext = stringArray;

      XSDocumentInfo[] xsDocumentInfo = new XSDocumentInfo[fKeyrefStackPos
          + INC_KEYREF_STACK_AMOUNT];
      System.arraycopy(fKeyrefsMapXSDocumentInfo, 0, xsDocumentInfo, 0, fKeyrefStackPos);
      fKeyrefsMapXSDocumentInfo = xsDocumentInfo;

    }
    fKeyrefs[fKeyrefStackPos] = keyrefToStore;
    fKeyrefElems[fKeyrefStackPos] = currElemDecl;
    fKeyrefNamespaceContext[fKeyrefStackPos] = schemaDoc.fNamespaceSupport
        .getEffectiveLocalContext();

    fKeyrefsMapXSDocumentInfo[fKeyrefStackPos++] = schemaDoc;
  } // storeKeyref (Element, XSDocumentInfo, XSElementDecl): void


  /**
   * resolveSchema method is responsible for resolving location of the schema (using
   * XMLEntityResolver), and if it was succefully resolved getting the schema Document.
   *
   * @return A schema Element or null.
   */
  private Element resolveSchema(XSDDescription desc, boolean mustResolve,
      Element referElement, boolean usePairs) {
    XMLInputSource schemaSource = null;
    try {
      Map pairs = usePairs ? fLocationPairs : EMPTY_TABLE;
      schemaSource = XMLSchemaLoader.resolveDocument(desc, pairs, fEntityResolver);
    } catch (IOException ex) {
      if (mustResolve) {
        reportSchemaError("schema_reference.4",
            new Object[]{desc.getLocationHints()[0]},
            referElement);
      } else {
        reportSchemaWarning("schema_reference.4",
            new Object[]{desc.getLocationHints()[0]},
            referElement);
      }
    }
    if (schemaSource instanceof DOMInputSource) {
      return getSchemaDocument(desc.getTargetNamespace(), (DOMInputSource) schemaSource,
          mustResolve, desc.getContextType(), referElement);
    } // DOMInputSource
    else if (schemaSource instanceof SAXInputSource) {
      return getSchemaDocument(desc.getTargetNamespace(), (SAXInputSource) schemaSource,
          mustResolve, desc.getContextType(), referElement);
    } // SAXInputSource
    else if (schemaSource instanceof StAXInputSource) {
      return getSchemaDocument(desc.getTargetNamespace(), (StAXInputSource) schemaSource,
          mustResolve, desc.getContextType(), referElement);
    } // StAXInputSource
    else if (schemaSource instanceof XSInputSource) {
      return getSchemaDocument((XSInputSource) schemaSource, desc);
    } // XSInputSource
    return getSchemaDocument(desc.getTargetNamespace(), schemaSource, mustResolve,
        desc.getContextType(), referElement);
  } // getSchema(String, String, String, boolean, short):  Document

  private Element resolveSchema(XMLInputSource schemaSource, XSDDescription desc,
      boolean mustResolve, Element referElement) {

    if (schemaSource instanceof DOMInputSource) {
      return getSchemaDocument(desc.getTargetNamespace(), (DOMInputSource) schemaSource,
          mustResolve, desc.getContextType(), referElement);
    } // DOMInputSource
    else if (schemaSource instanceof SAXInputSource) {
      return getSchemaDocument(desc.getTargetNamespace(), (SAXInputSource) schemaSource,
          mustResolve, desc.getContextType(), referElement);
    } // SAXInputSource
    else if (schemaSource instanceof StAXInputSource) {
      return getSchemaDocument(desc.getTargetNamespace(), (StAXInputSource) schemaSource,
          mustResolve, desc.getContextType(), referElement);
    } // StAXInputSource
    else if (schemaSource instanceof XSInputSource) {
      return getSchemaDocument((XSInputSource) schemaSource, desc);
    } // XSInputSource
    return getSchemaDocument(desc.getTargetNamespace(), schemaSource, mustResolve,
        desc.getContextType(), referElement);
  }

  private XMLInputSource resolveSchemaSource(XSDDescription desc, boolean mustResolve,
      Element referElement, boolean usePairs) {

    XMLInputSource schemaSource = null;
    try {
      Map pairs = usePairs ? fLocationPairs : EMPTY_TABLE;
      schemaSource = XMLSchemaLoader.resolveDocument(desc, pairs, fEntityResolver);
    } catch (IOException ex) {
      if (mustResolve) {
        reportSchemaError("schema_reference.4",
            new Object[]{desc.getLocationHints()[0]},
            referElement);
      } else {
        reportSchemaWarning("schema_reference.4",
            new Object[]{desc.getLocationHints()[0]},
            referElement);
      }
    }

    return schemaSource;
  }

  /**
   * getSchemaDocument method uses XMLInputSource to parse a schema document.
   *
   * @return A schema Element.
   */
  private Element getSchemaDocument(String schemaNamespace, XMLInputSource schemaSource,
      boolean mustResolve, short referType, Element referElement) {

    boolean hasInput = true;
    IOException exception = null;
    // contents of this method will depend on the system we adopt for entity resolution--i.e., XMLEntityHandler, EntityHandler, etc.
    Element schemaElement = null;
    try {
      // when the system id and byte stream and character stream
      // of the input source are all null, it's
      // impossible to find the schema document. so we skip in
      // this case. otherwise we'll receive some NPE or
      // file not found errors. but schemaHint=="" is perfectly
      // legal for import.
      if (schemaSource != null &&
          (schemaSource.getSystemId() != null ||
              schemaSource.getByteStream() != null ||
              schemaSource.getCharacterStream() != null)) {

        // When the system id of the input source is used, first try to
        // expand it, and check whether the same document has been
        // parsed before. If so, return the document corresponding to
        // that system id.
        XSDKey key = null;
        String schemaId = null;
        if (referType != XSDDescription.CONTEXT_PREPARSE) {
          schemaId = XMLEntityManager
              .expandSystemId(schemaSource.getSystemId(), schemaSource.getBaseSystemId(), false);
          key = new XSDKey(schemaId, referType, schemaNamespace);
          if ((schemaElement = (Element) fTraversed.get(key)) != null) {
            fLastSchemaWasDuplicate = true;
            return schemaElement;
          }
          if (referType == XSDDescription.CONTEXT_IMPORT
              || referType == XSDDescription.CONTEXT_INCLUDE
              || referType == XSDDescription.CONTEXT_REDEFINE) {
            String accessError = SecuritySupport
                .checkAccess(schemaId, fAccessExternalSchema, Constants.ACCESS_EXTERNAL_ALL);
            if (accessError != null) {
              reportSchemaFatalError("schema_reference.access",
                  new Object[]{SecuritySupport.sanitizePath(schemaId), accessError},
                  referElement);
            }
          }
        }

        fSchemaParser.parse(schemaSource);
        Document schemaDocument = fSchemaParser.getDocument();
        schemaElement = schemaDocument != null ? DOMUtil.getRoot(schemaDocument) : null;
        return getSchemaDocument0(key, schemaId, schemaElement);
      } else {
        hasInput = false;
      }
    } catch (IOException ex) {
      exception = ex;
    }
    return getSchemaDocument1(mustResolve, hasInput, schemaSource, referElement, exception);
  } // getSchemaDocument(String, XMLInputSource, boolean, short, Element): Element

  /**
   * getSchemaDocument method uses SAXInputSource to parse a schema document.
   *
   * @return A schema Element.
   */
  private Element getSchemaDocument(String schemaNamespace, SAXInputSource schemaSource,
      boolean mustResolve, short referType, Element referElement) {
    XMLReader parser = schemaSource.getXMLReader();
    InputSource inputSource = schemaSource.getInputSource();
    boolean hasInput = true;
    IOException exception = null;
    Element schemaElement = null;
    try {
      if (inputSource != null &&
          (inputSource.getSystemId() != null ||
              inputSource.getByteStream() != null ||
              inputSource.getCharacterStream() != null)) {

        // check whether the same document has been parsed before.
        // If so, return the document corresponding to that system id.
        XSDKey key = null;
        String schemaId = null;
        if (referType != XSDDescription.CONTEXT_PREPARSE) {
          schemaId = XMLEntityManager
              .expandSystemId(inputSource.getSystemId(), schemaSource.getBaseSystemId(), false);
          key = new XSDKey(schemaId, referType, schemaNamespace);
          if ((schemaElement = (Element) fTraversed.get(key)) != null) {
            fLastSchemaWasDuplicate = true;
            return schemaElement;
          }
        }

        boolean namespacePrefixes = false;
        if (parser != null) {
          try {
            namespacePrefixes = parser.getFeature(NAMESPACE_PREFIXES);
          } catch (SAXException se) {
          }
        } else {
          try {
            parser = XMLReaderFactory.createXMLReader();
          }
          // If something went wrong with the factory
          // just use our own SAX parser.
          catch (SAXException se) {
            parser = new SAXParser();
          }
          try {
            parser.setFeature(NAMESPACE_PREFIXES, true);
            namespacePrefixes = true;
            // If this is a Xerces SAX parser set the security manager if there is one
            if (parser instanceof SAXParser) {
              Object securityManager = fSchemaParser.getProperty(SECURITY_MANAGER);
              if (securityManager != null) {
                parser.setProperty(SECURITY_MANAGER, securityManager);
              }
            }
          } catch (SAXException se) {
          }

          try {
            parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, fAccessExternalDTD);
          } catch (SAXNotRecognizedException exc) {
            System.err.println("Warning: " + parser.getClass().getName() + ": " +
                exc.getMessage());
          }
        }
        // If XML names and Namespace URIs are already internalized we
        // can avoid running them through the SymbolTable.
        boolean stringsInternalized = false;
        try {
          stringsInternalized = parser.getFeature(STRING_INTERNING);
        } catch (SAXException exc) {
          // The feature isn't recognized or getting it is not supported.
          // In either case, assume that strings are not internalized.
        }
        if (fXSContentHandler == null) {
          fXSContentHandler = new SchemaContentHandler();
        }
        fXSContentHandler.reset(fSchemaParser, fSymbolTable,
            namespacePrefixes, stringsInternalized);
        parser.setContentHandler(fXSContentHandler);
        parser.setErrorHandler(fErrorReporter.getSAXErrorHandler());

        parser.parse(inputSource);
        // Disconnect the schema loader and other objects from the XMLReader
        try {
          parser.setContentHandler(null);
          parser.setErrorHandler(null);
        }
        // Ignore any exceptions thrown by the XMLReader. Old versions of SAX
        // required an XMLReader to throw a NullPointerException if an attempt
        // to set a handler to null was made.
        catch (Exception e) {
        }

        Document schemaDocument = fXSContentHandler.getDocument();
        schemaElement = schemaDocument != null ? DOMUtil.getRoot(schemaDocument) : null;
        return getSchemaDocument0(key, schemaId, schemaElement);
      } else {
        hasInput = false;
      }
    } catch (SAXParseException spe) {
      throw SAX2XNIUtil.createXMLParseException0(spe);
    } catch (SAXException se) {
      throw SAX2XNIUtil.createXNIException0(se);
    } catch (IOException ioe) {
      exception = ioe;
    }
    return getSchemaDocument1(mustResolve, hasInput, schemaSource, referElement, exception);
  } // getSchemaDocument(String, SAXInputSource, boolean, short, Element): Element

  /**
   * getSchemaDocument method uses DOMInputSource to parse a schema document.
   *
   * @return A schema Element.
   */
  private Element getSchemaDocument(String schemaNamespace, DOMInputSource schemaSource,
      boolean mustResolve, short referType, Element referElement) {
    boolean hasInput = true;
    IOException exception = null;
    Element schemaElement = null;
    Element schemaRootElement = null;

    final Node node = schemaSource.getNode();
    short nodeType = -1;
    if (node != null) {
      nodeType = node.getNodeType();
      if (nodeType == Node.DOCUMENT_NODE) {
        schemaRootElement = DOMUtil.getRoot((Document) node);
      } else if (nodeType == Node.ELEMENT_NODE) {
        schemaRootElement = (Element) node;
      }
    }

    try {
      if (schemaRootElement != null) {
        // check whether the same document has been parsed before.
        // If so, return the document corresponding to that system id.
        XSDKey key = null;
        String schemaId = null;
        if (referType != XSDDescription.CONTEXT_PREPARSE) {
          schemaId = XMLEntityManager
              .expandSystemId(schemaSource.getSystemId(), schemaSource.getBaseSystemId(), false);
          boolean isDocument = (nodeType == Node.DOCUMENT_NODE);
          if (!isDocument) {
            Node parent = schemaRootElement.getParentNode();
            if (parent != null) {
              isDocument = (parent.getNodeType() == Node.DOCUMENT_NODE);
            }
          }
          if (isDocument) {
            key = new XSDKey(schemaId, referType, schemaNamespace);
            if ((schemaElement = (Element) fTraversed.get(key)) != null) {
              fLastSchemaWasDuplicate = true;
              return schemaElement;
            }
          }
        }

        schemaElement = schemaRootElement;
        return getSchemaDocument0(key, schemaId, schemaElement);
      } else {
        hasInput = false;
      }
    } catch (IOException ioe) {
      exception = ioe;
    }
    return getSchemaDocument1(mustResolve, hasInput, schemaSource, referElement, exception);
  } // getSchemaDocument(String, DOMInputSource, boolean, short, Element): Element

  /**
   * getSchemaDocument method uses StAXInputSource to parse a schema document.
   *
   * @return A schema Element.
   */
  private Element getSchemaDocument(String schemaNamespace, StAXInputSource schemaSource,
      boolean mustResolve, short referType, Element referElement) {
    IOException exception = null;
    Element schemaElement = null;
    try {
      final boolean consumeRemainingContent = schemaSource.shouldConsumeRemainingContent();
      final XMLStreamReader streamReader = schemaSource.getXMLStreamReader();
      final XMLEventReader eventReader = schemaSource.getXMLEventReader();

      // check whether the same document has been parsed before.
      // If so, return the document corresponding to that system id.
      XSDKey key = null;
      String schemaId = null;
      if (referType != XSDDescription.CONTEXT_PREPARSE) {
        schemaId = XMLEntityManager
            .expandSystemId(schemaSource.getSystemId(), schemaSource.getBaseSystemId(), false);
        boolean isDocument = consumeRemainingContent;
        if (!isDocument) {
          if (streamReader != null) {
            isDocument = (streamReader.getEventType() == XMLStreamReader.START_DOCUMENT);
          } else {
            isDocument = eventReader.peek().isStartDocument();
          }
        }
        if (isDocument) {
          key = new XSDKey(schemaId, referType, schemaNamespace);
          if ((schemaElement = (Element) fTraversed.get(key)) != null) {
            fLastSchemaWasDuplicate = true;
            return schemaElement;
          }
        }
      }

      if (fStAXSchemaParser == null) {
        fStAXSchemaParser = new StAXSchemaParser();
      }
      fStAXSchemaParser.reset(fSchemaParser, fSymbolTable);

      if (streamReader != null) {
        fStAXSchemaParser.parse(streamReader);
        if (consumeRemainingContent) {
          while (streamReader.hasNext()) {
            streamReader.next();
          }
        }
      } else {
        fStAXSchemaParser.parse(eventReader);
        if (consumeRemainingContent) {
          while (eventReader.hasNext()) {
            eventReader.nextEvent();
          }
        }
      }
      Document schemaDocument = fStAXSchemaParser.getDocument();
      schemaElement = schemaDocument != null ? DOMUtil.getRoot(schemaDocument) : null;
      return getSchemaDocument0(key, schemaId, schemaElement);
    } catch (XMLStreamException e) {
      StAXLocationWrapper slw = new StAXLocationWrapper();
      slw.setLocation(e.getLocation());
      throw new XMLParseException(slw, e.getMessage(), e);
    } catch (IOException e) {
      exception = e;
    }
    return getSchemaDocument1(mustResolve, true, schemaSource, referElement, exception);
  } // getSchemaDocument(String, StAXInputSource, boolean, short, Element): Element

  /**
   * Code shared between the various getSchemaDocument() methods which
   * stores mapping information for the document.
   */
  private Element getSchemaDocument0(XSDKey key, String schemaId, Element schemaElement) {
    // now we need to store the mapping information from system id
    // to the document. also from the document to the system id.
    if (key != null) {
      fTraversed.put(key, schemaElement);
    }
    if (schemaId != null) {
      fDoc2SystemId.put(schemaElement, schemaId);
    }
    fLastSchemaWasDuplicate = false;
    return schemaElement;
  } // getSchemaDocument0(XSDKey, String, Element): Element

  /**
   * Error handling code shared between the various getSchemaDocument() methods.
   */
  private Element getSchemaDocument1(boolean mustResolve, boolean hasInput,
      XMLInputSource schemaSource, Element referElement, IOException ioe) {
    // either an error occured (exception), or empty input source was
    // returned, we need to report an error or a warning
    if (mustResolve) {
      if (hasInput) {
        reportSchemaError("schema_reference.4",
            new Object[]{schemaSource.getSystemId()},
            referElement, ioe);
      } else {
        reportSchemaError("schema_reference.4",
            new Object[]{schemaSource == null ? "" : schemaSource.getSystemId()},
            referElement, ioe);
      }
    } else if (hasInput) {
      reportSchemaWarning("schema_reference.4",
          new Object[]{schemaSource.getSystemId()},
          referElement, ioe);
    }

    fLastSchemaWasDuplicate = false;
    return null;
  } // getSchemaDocument1(boolean, boolean, XMLInputSource, Element): Element

  /**
   * getSchemaDocument method uses XMLInputSource to parse a schema document.
   *
   * @return A schema Element.
   */
  private Element getSchemaDocument(XSInputSource schemaSource, XSDDescription desc) {

    SchemaGrammar[] grammars = schemaSource.getGrammars();
    short referType = desc.getContextType();

    if (grammars != null && grammars.length > 0) {
      Vector expandedGrammars = expandGrammars(grammars);
      // check for existing grammars in our bucket
      // and if there exist any, and namespace growth is
      // not enabled - we do nothing
      if (fNamespaceGrowth || !existingGrammars(expandedGrammars)) {
        addGrammars(expandedGrammars);
        if (referType == XSDDescription.CONTEXT_PREPARSE) {
          desc.setTargetNamespace(grammars[0].getTargetNamespace());
        }
      }
    } else {
      XSObject[] components = schemaSource.getComponents();
      if (components != null && components.length > 0) {
        Map<String, Vector> importDependencies = new HashMap();
        Vector expandedComponents = expandComponents(components, importDependencies);
        if (fNamespaceGrowth || canAddComponents(expandedComponents)) {
          addGlobalComponents(expandedComponents, importDependencies);
          if (referType == XSDDescription.CONTEXT_PREPARSE) {
            desc.setTargetNamespace(components[0].getNamespace());
          }
        }
      }
    }
    return null;
  } // getSchemaDocument(String, XSInputSource, boolean, short, Element): Element

  private Vector expandGrammars(SchemaGrammar[] grammars) {
    Vector currGrammars = new Vector();

    for (int i = 0; i < grammars.length; i++) {
      if (!currGrammars.contains(grammars[i])) {
        currGrammars.add(grammars[i]);
      }
    }

    // for all (recursively) imported grammars
    SchemaGrammar sg1, sg2;
    Vector gs;
    for (int i = 0; i < currGrammars.size(); i++) {
      // get the grammar
      sg1 = (SchemaGrammar) currGrammars.elementAt(i);
      // we need to add grammars imported by sg1 too
      gs = sg1.getImportedGrammars();
      // for all grammars imported by sg2, but not in the vector
      // we add them to the vector
      if (gs == null) {
        continue;
      }

      for (int j = gs.size() - 1; j >= 0; j--) {
        sg2 = (SchemaGrammar) gs.elementAt(j);
        if (!currGrammars.contains(sg2)) {
          currGrammars.addElement(sg2);
        }
      }
    }

    return currGrammars;
  }

  private boolean existingGrammars(Vector grammars) {
    int length = grammars.size();
    final XSDDescription desc = new XSDDescription();

    for (int i = 0; i < length; i++) {
      final SchemaGrammar sg1 = (SchemaGrammar) grammars.elementAt(i);
      desc.setNamespace(sg1.getTargetNamespace());

      final SchemaGrammar sg2 = findGrammar(desc, false);
      if (sg2 != null) {
        return true;
      }
    }

    return false;
  }

  private boolean canAddComponents(Vector components) {
    final int size = components.size();
    final XSDDescription desc = new XSDDescription();
    for (int i = 0; i < size; i++) {
      XSObject component = (XSObject) components.elementAt(i);
      if (!canAddComponent(component, desc)) {
        return false;
      }
    }
    return true;
  }

  private boolean canAddComponent(XSObject component, XSDDescription desc) {
    desc.setNamespace(component.getNamespace());

    final SchemaGrammar sg = findGrammar(desc, false);
    if (sg == null) {
      return true;
    } else if (sg.isImmutable()) {
      return false;
    }

    short componentType = component.getType();
    final String name = component.getName();

    switch (componentType) {
      case XSConstants.TYPE_DEFINITION:
        if (sg.getGlobalTypeDecl(name) == component) {
          return true;
        }
        break;
      case XSConstants.ATTRIBUTE_DECLARATION:
        if (sg.getGlobalAttributeDecl(name) == component) {
          return true;
        }
        break;
      case XSConstants.ATTRIBUTE_GROUP:
        if (sg.getGlobalAttributeDecl(name) == component) {
          return true;
        }
        break;
      case XSConstants.ELEMENT_DECLARATION:
        if (sg.getGlobalElementDecl(name) == component) {
          return true;
        }
        break;
      case XSConstants.MODEL_GROUP_DEFINITION:
        if (sg.getGlobalGroupDecl(name) == component) {
          return true;
        }
        break;
      case XSConstants.NOTATION_DECLARATION:
        if (sg.getGlobalNotationDecl(name) == component) {
          return true;
        }
        break;
      case XSConstants.IDENTITY_CONSTRAINT:
      case XSConstants.ATTRIBUTE_USE:
      default:
        return true;
    }
    return false;
  }

  private void addGrammars(Vector grammars) {
    int length = grammars.size();
    XSDDescription desc = new XSDDescription();

    for (int i = 0; i < length; i++) {
      final SchemaGrammar sg1 = (SchemaGrammar) grammars.elementAt(i);
      desc.setNamespace(sg1.getTargetNamespace());

      final SchemaGrammar sg2 = findGrammar(desc, fNamespaceGrowth);
      if (sg1 != sg2) {
        addGrammarComponents(sg1, sg2);
      }
    }
  }

  private void addGrammarComponents(SchemaGrammar srcGrammar, SchemaGrammar dstGrammar) {
    if (dstGrammar == null) {
      createGrammarFrom(srcGrammar);
      return;
    }

    SchemaGrammar tmpGrammar = dstGrammar;
    if (tmpGrammar.isImmutable()) {
      tmpGrammar = createGrammarFrom(dstGrammar);
    }

    // add any new locations
    addNewGrammarLocations(srcGrammar, tmpGrammar);

    // add any new imported grammars
    addNewImportedGrammars(srcGrammar, tmpGrammar);

    // add any new global components
    addNewGrammarComponents(srcGrammar, tmpGrammar);
  }

  private SchemaGrammar createGrammarFrom(SchemaGrammar grammar) {
    SchemaGrammar newGrammar = new SchemaGrammar(grammar);
    fGrammarBucket.putGrammar(newGrammar);
    // update all the grammars in the bucket to point to the new grammar.
    updateImportListWith(newGrammar);
    // update import list of the new grammar
    updateImportListFor(newGrammar);
    return newGrammar;
  }

  private void addNewGrammarLocations(SchemaGrammar srcGrammar, SchemaGrammar dstGrammar) {
    final StringList locations = srcGrammar.getDocumentLocations();
    final int locSize = locations.size();
    final StringList locations2 = dstGrammar.getDocumentLocations();

    for (int i = 0; i < locSize; i++) {
      String loc = locations.item(i);
      if (!locations2.contains(loc)) {
        dstGrammar.addDocument(null, loc);
      }
    }
  }

  private void addNewImportedGrammars(SchemaGrammar srcGrammar, SchemaGrammar dstGrammar) {
    final Vector igs1 = srcGrammar.getImportedGrammars();
    if (igs1 != null) {
      Vector igs2 = dstGrammar.getImportedGrammars();

      if (igs2 == null) {
        igs2 = ((Vector) igs1.clone());
        dstGrammar.setImportedGrammars(igs2);
      } else {
        updateImportList(igs1, igs2);
      }
    }
  }

  private void updateImportList(Vector importedSrc, Vector importedDst) {
    final int size = importedSrc.size();

    for (int i = 0; i < size; i++) {
      final SchemaGrammar sg = (SchemaGrammar) importedSrc.elementAt(i);
      if (!containedImportedGrammar(importedDst, sg)) {
        importedDst.add(sg);
      }
    }
  }

  private void addNewGrammarComponents(SchemaGrammar srcGrammar, SchemaGrammar dstGrammar) {
    dstGrammar.resetComponents();
    addGlobalElementDecls(srcGrammar, dstGrammar);
    addGlobalAttributeDecls(srcGrammar, dstGrammar);
    addGlobalAttributeGroupDecls(srcGrammar, dstGrammar);
    addGlobalGroupDecls(srcGrammar, dstGrammar);
    addGlobalTypeDecls(srcGrammar, dstGrammar);
    addGlobalNotationDecls(srcGrammar, dstGrammar);
  }

  private void addGlobalElementDecls(SchemaGrammar srcGrammar, SchemaGrammar dstGrammar) {
    XSNamedMap components = srcGrammar.getComponents(XSConstants.ELEMENT_DECLARATION);
    int len = components.getLength();
    XSElementDecl srcDecl, dstDecl;

    // add global components
    for (int i = 0; i < len; i++) {
      srcDecl = (XSElementDecl) components.item(i);
      dstDecl = dstGrammar.getGlobalElementDecl(srcDecl.getName());
      if (dstDecl == null) {
        dstGrammar.addGlobalElementDecl(srcDecl);
      } else if (dstDecl != srcDecl) {
        // TODO: if not tolerating duplicate, generate an error message
      }
    }

    // add any extended (duplicate) global components
    ObjectList componentsExt = srcGrammar.getComponentsExt(XSConstants.ELEMENT_DECLARATION);
    len = componentsExt.getLength();

    for (int i = 0; i < len; i += 2) {
      final String key = (String) componentsExt.item(i);
      final int index = key.indexOf(',');
      final String location = key.substring(0, index);
      final String name = key.substring(index + 1, key.length());

      srcDecl = (XSElementDecl) componentsExt.item(i + 1);
      dstDecl = dstGrammar.getGlobalElementDecl(name, location);
      if (dstDecl == null) {
        dstGrammar.addGlobalElementDecl(srcDecl, location);
      } else if (dstDecl != srcDecl) {
        // TODO: if not tolerating duplicate, generate an error message
      }
    }
  }

  private void addGlobalAttributeDecls(SchemaGrammar srcGrammar, SchemaGrammar dstGrammar) {
    XSNamedMap components = srcGrammar.getComponents(XSConstants.ATTRIBUTE_DECLARATION);
    int len = components.getLength();
    XSAttributeDecl srcDecl, dstDecl;

    // add global components
    for (int i = 0; i < len; i++) {
      srcDecl = (XSAttributeDecl) components.item(i);
      dstDecl = dstGrammar.getGlobalAttributeDecl(srcDecl.getName());
      if (dstDecl == null) {
        dstGrammar.addGlobalAttributeDecl(srcDecl);
      } else if (dstDecl != srcDecl && !fTolerateDuplicates) {
        reportSharingError(srcDecl.getNamespace(), srcDecl.getName());
      }
    }

    // add any extended (duplicate) global components
    ObjectList componentsExt = srcGrammar.getComponentsExt(XSConstants.ATTRIBUTE_DECLARATION);
    len = componentsExt.getLength();

    for (int i = 0; i < len; i += 2) {
      final String key = (String) componentsExt.item(i);
      final int index = key.indexOf(',');
      final String location = key.substring(0, index);
      final String name = key.substring(index + 1, key.length());

      srcDecl = (XSAttributeDecl) componentsExt.item(i + 1);
      dstDecl = dstGrammar.getGlobalAttributeDecl(name, location);
      if (dstDecl == null) {
        dstGrammar.addGlobalAttributeDecl(srcDecl, location);
      }
      // REVISIT - do we report an error?
      else if (dstDecl != srcDecl) {
      }
    }
  }

  private void addGlobalAttributeGroupDecls(SchemaGrammar srcGrammar, SchemaGrammar dstGrammar) {
    XSNamedMap components = srcGrammar.getComponents(XSConstants.ATTRIBUTE_GROUP);
    int len = components.getLength();
    XSAttributeGroupDecl srcDecl, dstDecl;

    // add global components
    for (int i = 0; i < len; i++) {
      srcDecl = (XSAttributeGroupDecl) components.item(i);
      dstDecl = dstGrammar.getGlobalAttributeGroupDecl(srcDecl.getName());
      if (dstDecl == null) {
        dstGrammar.addGlobalAttributeGroupDecl(srcDecl);
      } else if (dstDecl != srcDecl && !fTolerateDuplicates) {
        reportSharingError(srcDecl.getNamespace(), srcDecl.getName());
      }
    }

    // add any extended (duplicate) global components
    ObjectList componentsExt = srcGrammar.getComponentsExt(XSConstants.ATTRIBUTE_GROUP);
    len = componentsExt.getLength();

    for (int i = 0; i < len; i += 2) {
      final String key = (String) componentsExt.item(i);
      final int index = key.indexOf(',');
      final String location = key.substring(0, index);
      final String name = key.substring(index + 1, key.length());

      srcDecl = (XSAttributeGroupDecl) componentsExt.item(i + 1);
      dstDecl = dstGrammar.getGlobalAttributeGroupDecl(name, location);
      if (dstDecl == null) {
        dstGrammar.addGlobalAttributeGroupDecl(srcDecl, location);
      }
      // REVISIT - do we report an error?
      else if (dstDecl != srcDecl) {
      }
    }
  }

  private void addGlobalNotationDecls(SchemaGrammar srcGrammar, SchemaGrammar dstGrammar) {
    XSNamedMap components = srcGrammar.getComponents(XSConstants.NOTATION_DECLARATION);
    int len = components.getLength();
    XSNotationDecl srcDecl, dstDecl;

    // add global components
    for (int i = 0; i < len; i++) {
      srcDecl = (XSNotationDecl) components.item(i);
      dstDecl = dstGrammar.getGlobalNotationDecl(srcDecl.getName());
      if (dstDecl == null) {
        dstGrammar.addGlobalNotationDecl(srcDecl);
      } else if (dstDecl != srcDecl && !fTolerateDuplicates) {
        reportSharingError(srcDecl.getNamespace(), srcDecl.getName());
      }
    }

    // add any extended (duplicate) global components
    ObjectList componentsExt = srcGrammar.getComponentsExt(XSConstants.NOTATION_DECLARATION);
    len = componentsExt.getLength();

    for (int i = 0; i < len; i += 2) {
      final String key = (String) componentsExt.item(i);
      final int index = key.indexOf(',');
      final String location = key.substring(0, index);
      final String name = key.substring(index + 1, key.length());

      srcDecl = (XSNotationDecl) componentsExt.item(i + 1);
      dstDecl = dstGrammar.getGlobalNotationDecl(name, location);
      if (dstDecl == null) {
        dstGrammar.addGlobalNotationDecl(srcDecl, location);
      }
      // REVISIT - do we report an error?
      else if (dstDecl != srcDecl) {
      }
    }
  }

  private void addGlobalGroupDecls(SchemaGrammar srcGrammar, SchemaGrammar dstGrammar) {
    XSNamedMap components = srcGrammar.getComponents(XSConstants.MODEL_GROUP_DEFINITION);
    int len = components.getLength();
    XSGroupDecl srcDecl, dstDecl;

    // add global components
    for (int i = 0; i < len; i++) {
      srcDecl = (XSGroupDecl) components.item(i);
      dstDecl = dstGrammar.getGlobalGroupDecl(srcDecl.getName());
      if (dstDecl == null) {
        dstGrammar.addGlobalGroupDecl(srcDecl);
      } else if (srcDecl != dstDecl && !fTolerateDuplicates) {
        reportSharingError(srcDecl.getNamespace(), srcDecl.getName());
      }
    }

    // add any extended (duplicate) global components
    ObjectList componentsExt = srcGrammar.getComponentsExt(XSConstants.MODEL_GROUP_DEFINITION);
    len = componentsExt.getLength();

    for (int i = 0; i < len; i += 2) {
      final String key = (String) componentsExt.item(i);
      final int index = key.indexOf(',');
      final String location = key.substring(0, index);
      final String name = key.substring(index + 1, key.length());

      srcDecl = (XSGroupDecl) componentsExt.item(i + 1);
      dstDecl = dstGrammar.getGlobalGroupDecl(name, location);
      if (dstDecl == null) {
        dstGrammar.addGlobalGroupDecl(srcDecl, location);
      }
      // REVIST - do we report an error?
      else if (dstDecl != srcDecl) {
      }
    }
  }

  private void addGlobalTypeDecls(SchemaGrammar srcGrammar, SchemaGrammar dstGrammar) {
    XSNamedMap components = srcGrammar.getComponents(XSConstants.TYPE_DEFINITION);
    int len = components.getLength();
    XSTypeDefinition srcDecl, dstDecl;

    // add global components
    for (int i = 0; i < len; i++) {
      srcDecl = (XSTypeDefinition) components.item(i);
      dstDecl = dstGrammar.getGlobalTypeDecl(srcDecl.getName());
      if (dstDecl == null) {
        dstGrammar.addGlobalTypeDecl(srcDecl);
      } else if (dstDecl != srcDecl && !fTolerateDuplicates) {
        reportSharingError(srcDecl.getNamespace(), srcDecl.getName());
      }
    }

    // add any extended (duplicate) global components
    ObjectList componentsExt = srcGrammar.getComponentsExt(XSConstants.TYPE_DEFINITION);
    len = componentsExt.getLength();

    for (int i = 0; i < len; i += 2) {
      final String key = (String) componentsExt.item(i);
      final int index = key.indexOf(',');
      final String location = key.substring(0, index);
      final String name = key.substring(index + 1, key.length());

      srcDecl = (XSTypeDefinition) componentsExt.item(i + 1);
      dstDecl = dstGrammar.getGlobalTypeDecl(name, location);
      if (dstDecl == null) {
        dstGrammar.addGlobalTypeDecl(srcDecl, location);
      }
      // REVISIT - do we report an error?
      else if (dstDecl != srcDecl) {
      }
    }
  }

  private Vector expandComponents(XSObject[] components, Map<String, Vector> dependencies) {
    Vector newComponents = new Vector();

    for (int i = 0; i < components.length; i++) {
      if (!newComponents.contains(components[i])) {
        newComponents.add(components[i]);
      }
    }

    for (int i = 0; i < newComponents.size(); i++) {
      final XSObject component = (XSObject) newComponents.elementAt(i);
      expandRelatedComponents(component, newComponents, dependencies);
    }

    return newComponents;
  }

  private void expandRelatedComponents(XSObject component, Vector componentList,
      Map<String, Vector> dependencies) {
    short componentType = component.getType();
    switch (componentType) {
      case XSConstants.TYPE_DEFINITION:
        expandRelatedTypeComponents((XSTypeDefinition) component, componentList,
            component.getNamespace(), dependencies);
        break;
      case XSConstants.ATTRIBUTE_DECLARATION:
        expandRelatedAttributeComponents((XSAttributeDeclaration) component, componentList,
            component.getNamespace(), dependencies);
        break;
      case XSConstants.ATTRIBUTE_GROUP:
        expandRelatedAttributeGroupComponents((XSAttributeGroupDefinition) component, componentList,
            component.getNamespace(), dependencies);
      case XSConstants.ELEMENT_DECLARATION:
        expandRelatedElementComponents((XSElementDeclaration) component, componentList,
            component.getNamespace(), dependencies);
        break;
      case XSConstants.MODEL_GROUP_DEFINITION:
        expandRelatedModelGroupDefinitionComponents((XSModelGroupDefinition) component,
            componentList, component.getNamespace(), dependencies);
      case XSConstants.ATTRIBUTE_USE:
        //expandRelatedAttributeUseComponents((XSAttributeUse)component, componentList, dependencies);
      case XSConstants.NOTATION_DECLARATION:
      case XSConstants.IDENTITY_CONSTRAINT:
      default:
        break;
    }
  }

  private void expandRelatedAttributeComponents(XSAttributeDeclaration decl, Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    addRelatedType(decl.getTypeDefinition(), componentList, namespace, dependencies);

        /*final XSComplexTypeDefinition enclosingType = decl.getEnclosingCTDefinition();
        if (enclosingType != null) {
            addRelatedType(enclosingType, componentList, namespace, dependencies);
        }*/
  }

  private void expandRelatedElementComponents(XSElementDeclaration decl, Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    addRelatedType(decl.getTypeDefinition(), componentList, namespace, dependencies);

        /*final XSTypeDefinition enclosingType = decl.getEnclosingCTDefinition();
        if (enclosingType != null) {
            addRelatedType(enclosingType, componentList, namespace, dependencies);
        }*/

    final XSElementDeclaration subElemDecl = decl.getSubstitutionGroupAffiliation();
    if (subElemDecl != null) {
      addRelatedElement(subElemDecl, componentList, namespace, dependencies);
    }
  }

  private void expandRelatedTypeComponents(XSTypeDefinition type, Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    if (type instanceof XSComplexTypeDecl) {
      expandRelatedComplexTypeComponents((XSComplexTypeDecl) type, componentList, namespace,
          dependencies);
    } else if (type instanceof XSSimpleTypeDecl) {
      expandRelatedSimpleTypeComponents((XSSimpleTypeDefinition) type, componentList, namespace,
          dependencies);
    }
  }

  private void expandRelatedModelGroupDefinitionComponents(XSModelGroupDefinition modelGroupDef,
      Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    expandRelatedModelGroupComponents(modelGroupDef.getModelGroup(), componentList, namespace,
        dependencies);
  }

  private void expandRelatedAttributeGroupComponents(XSAttributeGroupDefinition attrGroup,
      Vector componentList
      , String namespace, Map<String, Vector> dependencies) {
    expandRelatedAttributeUsesComponents(attrGroup.getAttributeUses(), componentList, namespace,
        dependencies);
  }

  private void expandRelatedComplexTypeComponents(XSComplexTypeDecl type, Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    addRelatedType(type.getBaseType(), componentList, namespace, dependencies);
    expandRelatedAttributeUsesComponents(type.getAttributeUses(), componentList, namespace,
        dependencies);
    final XSParticle particle = type.getParticle();
    if (particle != null) {
      expandRelatedParticleComponents(particle, componentList, namespace, dependencies);
    }
  }

  private void expandRelatedSimpleTypeComponents(XSSimpleTypeDefinition type, Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    final XSTypeDefinition baseType = type.getBaseType();
    if (baseType != null) {
      addRelatedType(baseType, componentList, namespace, dependencies);
    }

    final XSTypeDefinition itemType = type.getItemType();
    if (itemType != null) {
      addRelatedType(itemType, componentList, namespace, dependencies);
    }

    final XSTypeDefinition primitiveType = type.getPrimitiveType();
    if (primitiveType != null) {
      addRelatedType(primitiveType, componentList, namespace, dependencies);
    }

    final XSObjectList memberTypes = type.getMemberTypes();
    if (memberTypes.size() > 0) {
      for (int i = 0; i < memberTypes.size(); i++) {
        addRelatedType((XSTypeDefinition) memberTypes.item(i), componentList, namespace,
            dependencies);
      }
    }
  }

  private void expandRelatedAttributeUsesComponents(XSObjectList attrUses, Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    final int attrUseSize = (attrUses == null) ? 0 : attrUses.size();
    for (int i = 0; i < attrUseSize; i++) {
      expandRelatedAttributeUseComponents((XSAttributeUse) attrUses.item(i), componentList,
          namespace, dependencies);
    }
  }

  private void expandRelatedAttributeUseComponents(XSAttributeUse component, Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    addRelatedAttribute(component.getAttrDeclaration(), componentList, namespace, dependencies);
  }

  private void expandRelatedParticleComponents(XSParticle component, Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    XSTerm term = component.getTerm();
    switch (term.getType()) {
      case XSConstants.ELEMENT_DECLARATION:
        addRelatedElement((XSElementDeclaration) term, componentList, namespace, dependencies);
        break;
      case XSConstants.MODEL_GROUP:
        expandRelatedModelGroupComponents((XSModelGroup) term, componentList, namespace,
            dependencies);
        break;
      default:
        break;
    }
  }

  private void expandRelatedModelGroupComponents(XSModelGroup modelGroup, Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    XSObjectList particles = modelGroup.getParticles();
    final int length = (particles == null) ? 0 : particles.getLength();
    for (int i = 0; i < length; i++) {
      expandRelatedParticleComponents((XSParticle) particles.item(i), componentList, namespace,
          dependencies);
    }
  }

  private void addRelatedType(XSTypeDefinition type, Vector componentList, String namespace,
      Map<String, Vector> dependencies) {
    if (!type.getAnonymous()) {
      if (!type.getNamespace()
          .equals(SchemaSymbols.URI_SCHEMAFORSCHEMA)) { //REVISIT - do we use == instead
        if (!componentList.contains(type)) {
          final Vector importedNamespaces = findDependentNamespaces(namespace, dependencies);
          addNamespaceDependency(namespace, type.getNamespace(), importedNamespaces);
          componentList.add(type);
        }
      }
    } else {
      expandRelatedTypeComponents(type, componentList, namespace, dependencies);
    }
  }

  private void addRelatedElement(XSElementDeclaration decl, Vector componentList, String namespace,
      Map<String, Vector> dependencies) {
    if (decl.getScope() == XSConstants.SCOPE_GLOBAL) {
      if (!componentList.contains(decl)) {
        Vector importedNamespaces = findDependentNamespaces(namespace, dependencies);
        addNamespaceDependency(namespace, decl.getNamespace(), importedNamespaces);
        componentList.add(decl);
      }
    } else {
      expandRelatedElementComponents(decl, componentList, namespace, dependencies);
    }
  }

  private void addRelatedAttribute(XSAttributeDeclaration decl, Vector componentList,
      String namespace, Map<String, Vector> dependencies) {
    if (decl.getScope() == XSConstants.SCOPE_GLOBAL) {
      if (!componentList.contains(decl)) {
        Vector importedNamespaces = findDependentNamespaces(namespace, dependencies);
        addNamespaceDependency(namespace, decl.getNamespace(), importedNamespaces);
        componentList.add(decl);
      }
    } else {
      expandRelatedAttributeComponents(decl, componentList, namespace, dependencies);
    }
  }

  private void addGlobalComponents(Vector components, Map<String, Vector> importDependencies) {
    final XSDDescription desc = new XSDDescription();
    final int size = components.size();

    for (int i = 0; i < size; i++) {
      addGlobalComponent((XSObject) components.elementAt(i), desc);
    }
    updateImportDependencies(importDependencies);
  }

  private void addGlobalComponent(XSObject component, XSDDescription desc) {
    final String namespace = component.getNamespace();

    desc.setNamespace(namespace);
    final SchemaGrammar sg = getSchemaGrammar(desc);

    short componentType = component.getType();
    final String name = component.getName();

    switch (componentType) {
      case XSConstants.TYPE_DEFINITION:
        if (!((XSTypeDefinition) component).getAnonymous()) {
          if (sg.getGlobalTypeDecl(name) == null) {
            sg.addGlobalTypeDecl((XSTypeDefinition) component);
          }
          // store the declaration in the extended map, using an empty location
          if (sg.getGlobalTypeDecl(name, "") == null) {
            sg.addGlobalTypeDecl((XSTypeDefinition) component, "");
          }
        }
        break;
      case XSConstants.ATTRIBUTE_DECLARATION:
        if (((XSAttributeDecl) component).getScope() == XSAttributeDecl.SCOPE_GLOBAL) {
          if (sg.getGlobalAttributeDecl(name) == null) {
            sg.addGlobalAttributeDecl((XSAttributeDecl) component);
          }
          // store the declaration in the extended map, using an empty location
          if (sg.getGlobalAttributeDecl(name, "") == null) {
            sg.addGlobalAttributeDecl((XSAttributeDecl) component, "");
          }
        }
        break;
      case XSConstants.ATTRIBUTE_GROUP:
        if (sg.getGlobalAttributeDecl(name) == null) {
          sg.addGlobalAttributeGroupDecl((XSAttributeGroupDecl) component);
        }
        // store the declaration in the extended map, using an empty location
        if (sg.getGlobalAttributeDecl(name, "") == null) {
          sg.addGlobalAttributeGroupDecl((XSAttributeGroupDecl) component, "");
        }
        break;
      case XSConstants.ELEMENT_DECLARATION:
        if (((XSElementDecl) component).getScope() == XSElementDecl.SCOPE_GLOBAL) {
          sg.addGlobalElementDeclAll((XSElementDecl) component);

          if (sg.getGlobalElementDecl(name) == null) {
            sg.addGlobalElementDecl((XSElementDecl) component);
          }
          // store the declaration in the extended map, using an empty location
          if (sg.getGlobalElementDecl(name, "") == null) {
            sg.addGlobalElementDecl((XSElementDecl) component, "");
          }
        }
        break;
      case XSConstants.MODEL_GROUP_DEFINITION:
        if (sg.getGlobalGroupDecl(name) == null) {
          sg.addGlobalGroupDecl((XSGroupDecl) component);
        }
        // store the declaration in the extended map, using an empty location
        if (sg.getGlobalGroupDecl(name, "") == null) {
          sg.addGlobalGroupDecl((XSGroupDecl) component, "");
        }
        break;
      case XSConstants.NOTATION_DECLARATION:
        if (sg.getGlobalNotationDecl(name) == null) {
          sg.addGlobalNotationDecl((XSNotationDecl) component);
        }
        // store the declaration in the extended map, using an empty location
        if (sg.getGlobalNotationDecl(name, "") == null) {
          sg.addGlobalNotationDecl((XSNotationDecl) component, "");
        }
        break;
      case XSConstants.IDENTITY_CONSTRAINT:
      case XSConstants.ATTRIBUTE_USE:
      default:
        break;
    }
  }

  private void updateImportDependencies(Map<String, Vector> table) {
    if (table == null) {
      return;
    }
    String namespace;
    Vector importList;

    for (Map.Entry<String, Vector> entry : table.entrySet()) {
      namespace = entry.getKey();
      importList = entry.getValue();
      if (importList.size() > 0) {
        expandImportList(namespace, importList);
      }
    }
  }

  private void expandImportList(String namespace, Vector namespaceList) {
    SchemaGrammar sg = fGrammarBucket.getGrammar(namespace);
    // shouldn't be null
    if (sg != null) {
      Vector isgs = sg.getImportedGrammars();
      if (isgs == null) {
        isgs = new Vector();
        addImportList(sg, isgs, namespaceList);
        sg.setImportedGrammars(isgs);
      } else {
        updateImportList(sg, isgs, namespaceList);
      }
    }
  }

  private void addImportList(SchemaGrammar sg, Vector importedGrammars, Vector namespaceList) {
    final int size = namespaceList.size();
    SchemaGrammar isg;

    for (int i = 0; i < size; i++) {
      isg = fGrammarBucket.getGrammar((String) namespaceList.elementAt(i));
      if (isg != null) {
        importedGrammars.add(isg);
      } else {
        //REVIST: report an error message
      }
    }
  }

  private void updateImportList(SchemaGrammar sg, Vector importedGrammars, Vector namespaceList) {
    final int size = namespaceList.size();
    SchemaGrammar isg;

    for (int i = 0; i < size; i++) {
      isg = fGrammarBucket.getGrammar((String) namespaceList.elementAt(i));
      if (isg != null) {
        if (!containedImportedGrammar(importedGrammars, isg)) {
          importedGrammars.add(isg);
        }
      } else {
        //REVIST: report an error message
      }
    }
  }

  private boolean containedImportedGrammar(Vector importedGrammar, SchemaGrammar grammar) {
    final int size = importedGrammar.size();
    SchemaGrammar sg;

    for (int i = 0; i < size; i++) {
      sg = (SchemaGrammar) importedGrammar.elementAt(i);
      if (null2EmptyString(sg.getTargetNamespace())
          .equals(null2EmptyString(grammar.getTargetNamespace()))) {
        return true;
      }
    }
    return false;
  }

  // NOTE: always assuming that fNamespaceGrowth is enabled
  //       otherwise the grammar should have existed
  private SchemaGrammar getSchemaGrammar(XSDDescription desc) {
    SchemaGrammar sg = findGrammar(desc, fNamespaceGrowth);

    if (sg == null) {
      sg = new SchemaGrammar(desc.getNamespace(), desc.makeClone(), fSymbolTable);
      fGrammarBucket.putGrammar(sg);
    } else if (sg.isImmutable()) {
      sg = createGrammarFrom(sg);
    }

    return sg;
  }

  private Vector findDependentNamespaces(String namespace, Map table) {
    final String ns = null2EmptyString(namespace);
    Vector namespaceList = (Vector) getFromMap(table, ns);

    if (namespaceList == null) {
      namespaceList = new Vector();
      table.put(ns, namespaceList);
    }

    return namespaceList;
  }

  private void addNamespaceDependency(String namespace1, String namespace2, Vector list) {
    final String ns1 = null2EmptyString(namespace1);
    final String ns2 = null2EmptyString(namespace2);
    if (!ns1.equals(ns2)) {
      if (!list.contains(ns2)) {
        list.add(ns2);
      }
    }
  }

  private void reportSharingError(String namespace, String name) {
    final String qName = (namespace == null)
        ? "," + name : namespace + "," + name;

    reportSchemaError("sch-props-correct.2", new Object[]{qName}, null);
  }

  // initialize all the traversers.
  // this should only need to be called once during the construction
  // of this object; it creates the traversers that will be used to

  // construct schemaGrammars.
  private void createTraversers() {
    fAttributeChecker = new XSAttributeChecker(this);
    fAttributeGroupTraverser = new XSDAttributeGroupTraverser(this, fAttributeChecker);
    fAttributeTraverser = new XSDAttributeTraverser(this, fAttributeChecker);
    fComplexTypeTraverser = new XSDComplexTypeTraverser(this, fAttributeChecker);
    fElementTraverser = new XSDElementTraverser(this, fAttributeChecker);
    fGroupTraverser = new XSDGroupTraverser(this, fAttributeChecker);
    fKeyrefTraverser = new XSDKeyrefTraverser(this, fAttributeChecker);
    fNotationTraverser = new XSDNotationTraverser(this, fAttributeChecker);
    fSimpleTypeTraverser = new XSDSimpleTypeTraverser(this, fAttributeChecker);
    fUniqueOrKeyTraverser = new XSDUniqueOrKeyTraverser(this, fAttributeChecker);
    fWildCardTraverser = new XSDWildcardTraverser(this, fAttributeChecker);
  } // createTraversers()

  // before parsing a schema, need to clear registries associated with
  // parsing schemas
  void prepareForParse() {
    fTraversed.clear();
    fDoc2SystemId.clear();
    fHiddenNodes.clear();
    fLastSchemaWasDuplicate = false;
  }

  // before traversing a schema's parse tree, need to reset all traversers and
  // clear all registries
  void prepareForTraverse() {
    if (!registryEmpty) {
      fUnparsedAttributeRegistry.clear();
      fUnparsedAttributeGroupRegistry.clear();
      fUnparsedElementRegistry.clear();
      fUnparsedGroupRegistry.clear();
      fUnparsedIdentityConstraintRegistry.clear();
      fUnparsedNotationRegistry.clear();
      fUnparsedTypeRegistry.clear();

      fUnparsedAttributeRegistrySub.clear();
      fUnparsedAttributeGroupRegistrySub.clear();
      fUnparsedElementRegistrySub.clear();
      fUnparsedGroupRegistrySub.clear();
      fUnparsedIdentityConstraintRegistrySub.clear();
      fUnparsedNotationRegistrySub.clear();
      fUnparsedTypeRegistrySub.clear();
    }

    for (int i = 1; i <= TYPEDECL_TYPE; i++) {
      if (fUnparsedRegistriesExt[i] != null) {
        fUnparsedRegistriesExt[i].clear();
      }
    }

    fDependencyMap.clear();
    fDoc2XSDocumentMap.clear();
    if (fRedefine2XSDMap != null) {
      fRedefine2XSDMap.clear();
    }
    if (fRedefine2NSSupport != null) {
      fRedefine2NSSupport.clear();
    }
    fAllTNSs.removeAllElements();
    fImportMap.clear();
    fRoot = null;

    // clear local element stack
    for (int i = 0; i < fLocalElemStackPos; i++) {
      fParticle[i] = null;
      fLocalElementDecl[i] = null;
      fLocalElementDecl_schema[i] = null;
      fLocalElemNamespaceContext[i] = null;
    }
    fLocalElemStackPos = 0;

    // and do same for keyrefs.
    for (int i = 0; i < fKeyrefStackPos; i++) {
      fKeyrefs[i] = null;
      fKeyrefElems[i] = null;
      fKeyrefNamespaceContext[i] = null;
      fKeyrefsMapXSDocumentInfo[i] = null;
    }
    fKeyrefStackPos = 0;

    // create traversers if necessary
    if (fAttributeChecker == null) {
      createTraversers();
    }

    // reset traversers
    Locale locale = fErrorReporter.getLocale();
    fAttributeChecker.reset(fSymbolTable);
    fAttributeGroupTraverser.reset(fSymbolTable, fValidateAnnotations, locale);
    fAttributeTraverser.reset(fSymbolTable, fValidateAnnotations, locale);
    fComplexTypeTraverser.reset(fSymbolTable, fValidateAnnotations, locale);
    fElementTraverser.reset(fSymbolTable, fValidateAnnotations, locale);
    fGroupTraverser.reset(fSymbolTable, fValidateAnnotations, locale);
    fKeyrefTraverser.reset(fSymbolTable, fValidateAnnotations, locale);
    fNotationTraverser.reset(fSymbolTable, fValidateAnnotations, locale);
    fSimpleTypeTraverser.reset(fSymbolTable, fValidateAnnotations, locale);
    fUniqueOrKeyTraverser.reset(fSymbolTable, fValidateAnnotations, locale);
    fWildCardTraverser.reset(fSymbolTable, fValidateAnnotations, locale);

    fRedefinedRestrictedAttributeGroupRegistry.clear();
    fRedefinedRestrictedGroupRegistry.clear();

    fGlobalAttrDecls.clear();
    fGlobalAttrGrpDecls.clear();
    fGlobalElemDecls.clear();
    fGlobalGroupDecls.clear();
    fGlobalNotationDecls.clear();
    fGlobalIDConstraintDecls.clear();
    fGlobalTypeDecls.clear();
  }

  public void setDeclPool(XSDeclarationPool declPool) {
    fDeclPool = declPool;
  }

  public void setDVFactory(SchemaDVFactory dvFactory) {
    fDVFactory = dvFactory;
  }

  public SchemaDVFactory getDVFactory() {
    return fDVFactory;
  }

  public void reset(XMLComponentManager componentManager) {

    // set symbol table
    fSymbolTable = (SymbolTable) componentManager.getProperty(SYMBOL_TABLE);

    fSecureProcessing = null;
    if (componentManager != null) {
      fSecureProcessing = (XMLSecurityManager) componentManager
          .getProperty(SECURE_PROCESSING, null);
    }

    //set entity resolver
    fEntityResolver = (XMLEntityResolver) componentManager.getProperty(ENTITY_MANAGER);
    XMLEntityResolver er = (XMLEntityResolver) componentManager.getProperty(ENTITY_RESOLVER);
    if (er != null) {
      fSchemaParser.setEntityResolver(er);
    }

    // set error reporter
    fErrorReporter =
        (XMLErrorReporter) componentManager.getProperty(ERROR_REPORTER);
    try {
      XMLErrorHandler currErrorHandler = fErrorReporter.getErrorHandler();
      // Setting a parser property can be much more expensive
      // than checking its value.  Don't set the ERROR_HANDLER
      // or LOCALE properties unless they've actually changed.
      if (currErrorHandler != fSchemaParser.getProperty(ERROR_HANDLER)) {
        fSchemaParser.setProperty(ERROR_HANDLER,
            (currErrorHandler != null) ? currErrorHandler : new DefaultErrorHandler());
        if (fAnnotationValidator != null) {
          fAnnotationValidator.setProperty(ERROR_HANDLER,
              (currErrorHandler != null) ? currErrorHandler : new DefaultErrorHandler());
        }
      }
      Locale currentLocale = fErrorReporter.getLocale();
      if (currentLocale != fSchemaParser.getProperty(LOCALE)) {
        fSchemaParser.setProperty(LOCALE, currentLocale);
        if (fAnnotationValidator != null) {
          fAnnotationValidator.setProperty(LOCALE, currentLocale);
        }
      }
    } catch (XMLConfigurationException e) {
    }

    fValidateAnnotations = componentManager.getFeature(VALIDATE_ANNOTATIONS, false);
    fHonourAllSchemaLocations = componentManager.getFeature(HONOUR_ALL_SCHEMALOCATIONS, false);
    fNamespaceGrowth = componentManager.getFeature(NAMESPACE_GROWTH, false);
    fTolerateDuplicates = componentManager.getFeature(TOLERATE_DUPLICATES, false);

    try {
      fSchemaParser.setFeature(
          CONTINUE_AFTER_FATAL_ERROR,
          fErrorReporter.getFeature(CONTINUE_AFTER_FATAL_ERROR));
    } catch (XMLConfigurationException e) {
    }

    try {
      if (componentManager.getFeature(ALLOW_JAVA_ENCODINGS, false)) {
        fSchemaParser.setFeature(ALLOW_JAVA_ENCODINGS, true);
      }
    } catch (XMLConfigurationException e) {
    }
    try {
      if (componentManager.getFeature(STANDARD_URI_CONFORMANT_FEATURE, false)) {
        fSchemaParser.setFeature(STANDARD_URI_CONFORMANT_FEATURE, true);
      }
    } catch (XMLConfigurationException e) {
    }

    try {
      fGrammarPool =
          (XMLGrammarPool) componentManager.getProperty(XMLGRAMMAR_POOL);
    } catch (XMLConfigurationException e) {
      fGrammarPool = null;
    }
    // security features
    try {
      if (componentManager.getFeature(DISALLOW_DOCTYPE, false)) {
        fSchemaParser.setFeature(DISALLOW_DOCTYPE, true);
      }
    } catch (XMLConfigurationException e) {
    }
    try {
      Object security = componentManager.getProperty(SECURITY_MANAGER, null);
      if (security != null) {
        fSchemaParser.setProperty(SECURITY_MANAGER, security);
      }
    } catch (XMLConfigurationException e) {
    }

    XMLSecurityPropertyManager securityPropertyMgr = (XMLSecurityPropertyManager)
        componentManager.getProperty(XML_SECURITY_PROPERTY_MANAGER);
    //Passing on the setting to the parser
    fSchemaParser.setProperty(XML_SECURITY_PROPERTY_MANAGER, securityPropertyMgr);

    fAccessExternalDTD = securityPropertyMgr.getValue(
        XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD);

    fAccessExternalSchema = securityPropertyMgr.getValue(
        XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA);

  } // reset(XMLComponentManager)


  /**
   * Traverse all the deferred local elements. This method should be called
   * by traverseSchemas after we've done with all the global declarations.
   */
  void traverseLocalElements() {
    fElementTraverser.fDeferTraversingLocalElements = false;

    for (int i = 0; i < fLocalElemStackPos; i++) {
      Element currElem = fLocalElementDecl[i];
      //XSDocumentInfo currSchema = (XSDocumentInfo)fDoc2XSDocumentMap.get(DOMUtil.getDocument(currElem));
      //XSDocumentInfo currSchema = (XSDocumentInfo)fDoc2XSDocumentMap.get(DOMUtil.getRoot(DOMUtil.getDocument(currElem)));
      XSDocumentInfo currSchema = fLocalElementDecl_schema[i];
      SchemaGrammar currGrammar = fGrammarBucket.getGrammar(currSchema.fTargetNamespace);
      fElementTraverser
          .traverseLocal(fParticle[i], currElem, currSchema, currGrammar, fAllContext[i],
              fParent[i], fLocalElemNamespaceContext[i]);
      // If it's an empty particle, remove it from the containing component.
      if (fParticle[i].fType == XSParticleDecl.PARTICLE_EMPTY) {
        XSModelGroupImpl group = null;
        if (fParent[i] instanceof XSComplexTypeDecl) {
          XSParticle p = ((XSComplexTypeDecl) fParent[i]).getParticle();
          if (p != null) {
            group = (XSModelGroupImpl) p.getTerm();
          }
        } else {
          group = ((XSGroupDecl) fParent[i]).fModelGroup;
        }
        if (group != null) {
          removeParticle(group, fParticle[i]);
        }
      }
    }
  }

  private boolean removeParticle(XSModelGroupImpl group, XSParticleDecl particle) {
    XSParticleDecl member;
    for (int i = 0; i < group.fParticleCount; i++) {
      member = group.fParticles[i];
      if (member == particle) {
        for (int j = i; j < group.fParticleCount - 1; j++) {
          group.fParticles[j] = group.fParticles[j + 1];
        }
        group.fParticleCount--;
        return true;
      }
      if (member.fType == XSParticleDecl.PARTICLE_MODELGROUP) {
        if (removeParticle((XSModelGroupImpl) member.fValue, particle)) {
          return true;
        }
      }
    }
    return false;
  }

  // the purpose of this method is to keep up-to-date structures
  // we'll need for the feferred traversal of local elements.
  void fillInLocalElemInfo(Element elmDecl,
      XSDocumentInfo schemaDoc,
      int allContextFlags,
      XSObject parent,
      XSParticleDecl particle) {

    // if the stack is full, increase the size
    if (fParticle.length == fLocalElemStackPos) {
      // increase size
      XSParticleDecl[] newStackP = new XSParticleDecl[fLocalElemStackPos + INC_STACK_SIZE];
      System.arraycopy(fParticle, 0, newStackP, 0, fLocalElemStackPos);
      fParticle = newStackP;
      Element[] newStackE = new Element[fLocalElemStackPos + INC_STACK_SIZE];
      System.arraycopy(fLocalElementDecl, 0, newStackE, 0, fLocalElemStackPos);
      fLocalElementDecl = newStackE;
      XSDocumentInfo[] newStackE_schema = new XSDocumentInfo[fLocalElemStackPos + INC_STACK_SIZE];
      System.arraycopy(fLocalElementDecl_schema, 0, newStackE_schema, 0, fLocalElemStackPos);
      fLocalElementDecl_schema = newStackE_schema;
      int[] newStackI = new int[fLocalElemStackPos + INC_STACK_SIZE];
      System.arraycopy(fAllContext, 0, newStackI, 0, fLocalElemStackPos);
      fAllContext = newStackI;
      XSObject[] newStackC = new XSObject[fLocalElemStackPos + INC_STACK_SIZE];
      System.arraycopy(fParent, 0, newStackC, 0, fLocalElemStackPos);
      fParent = newStackC;
      String[][] newStackN = new String[fLocalElemStackPos + INC_STACK_SIZE][];
      System.arraycopy(fLocalElemNamespaceContext, 0, newStackN, 0, fLocalElemStackPos);
      fLocalElemNamespaceContext = newStackN;
    }

    fParticle[fLocalElemStackPos] = particle;
    fLocalElementDecl[fLocalElemStackPos] = elmDecl;
    fLocalElementDecl_schema[fLocalElemStackPos] = schemaDoc;
    fAllContext[fLocalElemStackPos] = allContextFlags;
    fParent[fLocalElemStackPos] = parent;
    fLocalElemNamespaceContext[fLocalElemStackPos++] = schemaDoc.fNamespaceSupport
        .getEffectiveLocalContext();
  } // end fillInLocalElemInfo(...)

  /**
   * This method makes sure that
   * if this component is being redefined that it lives in the
   * right schema.  It then renames the component correctly.  If it
   * detects a collision--a duplicate definition--then it complains.
   * Note that redefines must be handled carefully:  if there
   * is a collision, it may be because we're redefining something we know about
   * or because we've found the thing we're redefining.
   */
  void checkForDuplicateNames(String qName, int declType,
      Map<String, Element> registry, Map<String, XSDocumentInfo> registry_sub, Element currComp,
      XSDocumentInfo currSchema) {
    Object objElem = null;
    // REVISIT:  when we add derivation checking, we'll have to make
    // sure that ID constraint collisions don't necessarily result in error messages.
    if ((objElem = registry.get(qName)) == null) {
      // need to check whether we have a global declaration in the corresponding
      // grammar
      if (fNamespaceGrowth && !fTolerateDuplicates) {
        checkForDuplicateNames(qName, declType, currComp);
      }
      // just add it in!
      registry.put(qName, currComp);
      registry_sub.put(qName, currSchema);
    } else {
      Element collidingElem = (Element) objElem;
      XSDocumentInfo collidingElemSchema = (XSDocumentInfo) registry_sub.get(qName);
      if (collidingElem == currComp) {
        return;
      }
      Element elemParent = null;
      XSDocumentInfo redefinedSchema = null;
      // case where we've collided with a redefining element
      // (the parent of the colliding element is a redefine)
      boolean collidedWithRedefine = true;
      if ((DOMUtil.getLocalName((elemParent = DOMUtil.getParent(collidingElem)))
          .equals(SchemaSymbols.ELT_REDEFINE))) {
        redefinedSchema =
            (fRedefine2XSDMap != null) ? (XSDocumentInfo) (fRedefine2XSDMap.get(elemParent)) : null;
        // case where we're a redefining element.
      } else if ((DOMUtil.getLocalName(DOMUtil.getParent(currComp))
          .equals(SchemaSymbols.ELT_REDEFINE))) {
        redefinedSchema = collidingElemSchema;
        collidedWithRedefine = false;
      }
      if (redefinedSchema != null) { //redefinition involved somehow
        // If both components belong to the same document then
        // report an error and return.
        if (collidingElemSchema == currSchema) {
          reportSchemaError("sch-props-correct.2", new Object[]{qName}, currComp);
          return;
        }

        String newName = qName.substring(qName.lastIndexOf(',') + 1) + REDEF_IDENTIFIER;
        if (redefinedSchema == currSchema) { // object comp. okay here
          // now have to do some renaming...
          currComp.setAttribute(SchemaSymbols.ATT_NAME, newName);
          if (currSchema.fTargetNamespace == null) {
            registry.put("," + newName, currComp);
            registry_sub.put("," + newName, currSchema);
          } else {
            registry.put(currSchema.fTargetNamespace + "," + newName, currComp);
            registry_sub.put(currSchema.fTargetNamespace + "," + newName, currSchema);
          }
          // and take care of nested redefines by calling recursively:
          if (currSchema.fTargetNamespace == null) {
            checkForDuplicateNames("," + newName, declType, registry, registry_sub, currComp,
                currSchema);
          } else {
            checkForDuplicateNames(currSchema.fTargetNamespace + "," + newName, declType, registry,
                registry_sub, currComp, currSchema);
          }
        } else { // we may be redefining the wrong schema
          if (collidedWithRedefine) {
            if (currSchema.fTargetNamespace == null) {
              checkForDuplicateNames("," + newName, declType, registry, registry_sub, currComp,
                  currSchema);
            } else {
              checkForDuplicateNames(currSchema.fTargetNamespace + "," + newName, declType,
                  registry, registry_sub, currComp, currSchema);
            }
          } else {
            // error that redefined element in wrong schema
            reportSchemaError("sch-props-correct.2", new Object[]{qName}, currComp);
          }
        }
      } else {
        // we've just got a flat-out collision (we tolerate duplicate
        // declarations, only if they are defined in different schema
        // documents)
        if (!fTolerateDuplicates) {
          reportSchemaError("sch-props-correct.2", new Object[]{qName}, currComp);
        } else if (fUnparsedRegistriesExt[declType] != null) {
          if (fUnparsedRegistriesExt[declType].get(qName) == currSchema) {
            reportSchemaError("sch-props-correct.2", new Object[]{qName}, currComp);
          }
        }
      }
    }

    // store the lastest current document info
    if (fTolerateDuplicates) {
      if (fUnparsedRegistriesExt[declType] == null) {
        fUnparsedRegistriesExt[declType] = new HashMap();
      }
      fUnparsedRegistriesExt[declType].put(qName, currSchema);
    }

  } // checkForDuplicateNames(String, Hashtable, Element, XSDocumentInfo):void

  void checkForDuplicateNames(String qName, int declType, Element currComp) {
    int namespaceEnd = qName.indexOf(',');
    String namespace = qName.substring(0, namespaceEnd);
    SchemaGrammar grammar = fGrammarBucket.getGrammar(emptyString2Null(namespace));

    if (grammar != null) {
      Object obj = getGlobalDeclFromGrammar(grammar, declType, qName.substring(namespaceEnd + 1));
      if (obj != null) {
        reportSchemaError("sch-props-correct.2", new Object[]{qName}, currComp);
      }
    }
  }

  // the purpose of this method is to take the component of the
  // specified type and rename references to itself so that they
  // refer to the object being redefined.  It takes special care of
  // <group>s and <attributeGroup>s to ensure that information
  // relating to implicit restrictions is preserved for those
  // traversers.
  private void renameRedefiningComponents(XSDocumentInfo currSchema,
      Element child, String componentType,
      String oldName, String newName) {
    if (componentType.equals(SchemaSymbols.ELT_SIMPLETYPE)) {
      Element grandKid = DOMUtil.getFirstChildElement(child);
      if (grandKid == null) {
        reportSchemaError("src-redefine.5.a.a", null, child);
      } else {
        String grandKidName = DOMUtil.getLocalName(grandKid);
        if (grandKidName.equals(SchemaSymbols.ELT_ANNOTATION)) {
          grandKid = DOMUtil.getNextSiblingElement(grandKid);
        }
        if (grandKid == null) {
          reportSchemaError("src-redefine.5.a.a", null, child);
        } else {
          grandKidName = DOMUtil.getLocalName(grandKid);
          if (!grandKidName.equals(SchemaSymbols.ELT_RESTRICTION)) {
            reportSchemaError("src-redefine.5.a.b", new Object[]{grandKidName}, child);
          } else {
            Object[] attrs = fAttributeChecker.checkAttributes(grandKid, false, currSchema);
            QName derivedBase = (QName) attrs[XSAttributeChecker.ATTIDX_BASE];
            if (derivedBase == null ||
                derivedBase.uri != currSchema.fTargetNamespace ||
                !derivedBase.localpart.equals(oldName)) {
              reportSchemaError("src-redefine.5.a.c",
                  new Object[]{grandKidName,
                      (currSchema.fTargetNamespace == null ? "" : currSchema.fTargetNamespace)
                          + "," + oldName},
                  child);
            } else {
              // now we have to do the renaming...
              if (derivedBase.prefix != null && derivedBase.prefix.length() > 0) {
                grandKid.setAttribute(SchemaSymbols.ATT_BASE,
                    derivedBase.prefix + ":" + newName);
              } else {
                grandKid.setAttribute(SchemaSymbols.ATT_BASE, newName);
              }
              //                            return true;
            }
            fAttributeChecker.returnAttrArray(attrs, currSchema);
          }
        }
      }
    } else if (componentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) {
      Element grandKid = DOMUtil.getFirstChildElement(child);
      if (grandKid == null) {
        reportSchemaError("src-redefine.5.b.a", null, child);
      } else {
        if (DOMUtil.getLocalName(grandKid).equals(SchemaSymbols.ELT_ANNOTATION)) {
          grandKid = DOMUtil.getNextSiblingElement(grandKid);
        }
        if (grandKid == null) {
          reportSchemaError("src-redefine.5.b.a", null, child);
        } else {
          // have to go one more level down; let another pass worry whether complexType is valid.
          Element greatGrandKid = DOMUtil.getFirstChildElement(grandKid);
          if (greatGrandKid == null) {
            reportSchemaError("src-redefine.5.b.b", null, grandKid);
          } else {
            String greatGrandKidName = DOMUtil.getLocalName(greatGrandKid);
            if (greatGrandKidName.equals(SchemaSymbols.ELT_ANNOTATION)) {
              greatGrandKid = DOMUtil.getNextSiblingElement(greatGrandKid);
            }
            if (greatGrandKid == null) {
              reportSchemaError("src-redefine.5.b.b", null, grandKid);
            } else {
              greatGrandKidName = DOMUtil.getLocalName(greatGrandKid);
              if (!greatGrandKidName.equals(SchemaSymbols.ELT_RESTRICTION) &&
                  !greatGrandKidName.equals(SchemaSymbols.ELT_EXTENSION)) {
                reportSchemaError("src-redefine.5.b.c", new Object[]{greatGrandKidName},
                    greatGrandKid);
              } else {
                Object[] attrs = fAttributeChecker
                    .checkAttributes(greatGrandKid, false, currSchema);
                QName derivedBase = (QName) attrs[XSAttributeChecker.ATTIDX_BASE];
                if (derivedBase == null ||
                    derivedBase.uri != currSchema.fTargetNamespace ||
                    !derivedBase.localpart.equals(oldName)) {
                  reportSchemaError("src-redefine.5.b.d",
                      new Object[]{greatGrandKidName,
                          (currSchema.fTargetNamespace == null ? "" : currSchema.fTargetNamespace)
                              + "," + oldName},
                      greatGrandKid);
                } else {
                  // now we have to do the renaming...
                  if (derivedBase.prefix != null && derivedBase.prefix.length() > 0) {
                    greatGrandKid.setAttribute(SchemaSymbols.ATT_BASE,
                        derivedBase.prefix + ":" + newName);
                  } else {
                    greatGrandKid.setAttribute(SchemaSymbols.ATT_BASE,
                        newName);
                  }
                  //                                    return true;
                }
              }
            }
          }
        }
      }
    } else if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
      String processedBaseName = (currSchema.fTargetNamespace == null) ?
          "," + oldName : currSchema.fTargetNamespace + "," + oldName;
      int attGroupRefsCount = changeRedefineGroup(processedBaseName, componentType, newName, child,
          currSchema);
      if (attGroupRefsCount > 1) {
        reportSchemaError("src-redefine.7.1", new Object[]{new Integer(attGroupRefsCount)}, child);
      } else if (attGroupRefsCount == 1) {
        //                return true;
      } else if (currSchema.fTargetNamespace == null) {
        fRedefinedRestrictedAttributeGroupRegistry.put(processedBaseName, "," + newName);
      } else {
        fRedefinedRestrictedAttributeGroupRegistry
            .put(processedBaseName, currSchema.fTargetNamespace + "," + newName);
      }
    } else if (componentType.equals(SchemaSymbols.ELT_GROUP)) {
      String processedBaseName = (currSchema.fTargetNamespace == null) ?
          "," + oldName : currSchema.fTargetNamespace + "," + oldName;
      int groupRefsCount = changeRedefineGroup(processedBaseName, componentType, newName, child,
          currSchema);
      if (groupRefsCount > 1) {
        reportSchemaError("src-redefine.6.1.1", new Object[]{new Integer(groupRefsCount)}, child);
      } else if (groupRefsCount == 1) {
        //                return true;
      } else {
        if (currSchema.fTargetNamespace == null) {
          fRedefinedRestrictedGroupRegistry.put(processedBaseName, "," + newName);
        } else {
          fRedefinedRestrictedGroupRegistry
              .put(processedBaseName, currSchema.fTargetNamespace + "," + newName);
        }
      }
    } else {
      reportSchemaError("Internal-Error", new Object[]{
              "could not handle this particular <redefine>; please submit your schemas and instance document in a bug report!"},
          child);
    }
    // if we get here then we must have reported an error and failed somewhere...
    //        return false;
  } // renameRedefiningComponents(XSDocumentInfo, Element, String, String, String):void

  // this method takes a name of the form a:b, determines the URI mapped
  // to by a in the current SchemaNamespaceSupport object, and returns this
  // information in the form (nsURI,b) suitable for lookups in the global
  // decl Hashtables.
  // REVISIT: should have it return QName, instead of String. this would
  //          save lots of string concatenation time. we can use
  //          QName#equals() to compare two QNames, and use QName directly
  //          as a key to the SymbolHash.
  //          And when the DV's are ready to return compiled values from
  //          validate() method, we should just call QNameDV.validate()
  //          in this method.
  private String findQName(String name, XSDocumentInfo schemaDoc) {
    SchemaNamespaceSupport currNSMap = schemaDoc.fNamespaceSupport;
    int colonPtr = name.indexOf(':');
    String prefix = XMLSymbols.EMPTY_STRING;
    if (colonPtr > 0) {
      prefix = name.substring(0, colonPtr);
    }
    String uri = currNSMap.getURI(fSymbolTable.addSymbol(prefix));
    String localpart = (colonPtr == 0) ? name : name.substring(colonPtr + 1);
    if (prefix == XMLSymbols.EMPTY_STRING && uri == null && schemaDoc.fIsChameleonSchema) {
      uri = schemaDoc.fTargetNamespace;
    }
    if (uri == null) {
      return "," + localpart;
    }
    return uri + "," + localpart;
  } // findQName(String, XSDocumentInfo):  String

  // This function looks among the children of curr for an element of type elementSought.
  // If it finds one, it evaluates whether its ref attribute contains a reference
  // to originalQName.  If it does, it returns 1 + the value returned by
  // calls to itself on all other children.  In all other cases it returns 0 plus
  // the sum of the values returned by calls to itself on curr's children.
  // It also resets the value of ref so that it will refer to the renamed type from the schema
  // being redefined.
  private int changeRedefineGroup(String originalQName, String elementSought,
      String newName, Element curr, XSDocumentInfo schemaDoc) {
    int result = 0;
    for (Element child = DOMUtil.getFirstChildElement(curr);
        child != null; child = DOMUtil.getNextSiblingElement(child)) {
      String name = DOMUtil.getLocalName(child);
      if (!name.equals(elementSought)) {
        result += changeRedefineGroup(originalQName, elementSought, newName, child, schemaDoc);
      } else {
        String ref = child.getAttribute(SchemaSymbols.ATT_REF);
        if (ref.length() != 0) {
          String processedRef = findQName(ref, schemaDoc);
          if (originalQName.equals(processedRef)) {
            String prefix = XMLSymbols.EMPTY_STRING;
            int colonptr = ref.indexOf(":");
            if (colonptr > 0) {
              prefix = ref.substring(0, colonptr);
              child.setAttribute(SchemaSymbols.ATT_REF, prefix + ":" + newName);
            } else {
              child.setAttribute(SchemaSymbols.ATT_REF, newName);
            }
            result++;
            if (elementSought.equals(SchemaSymbols.ELT_GROUP)) {
              String minOccurs = child.getAttribute(SchemaSymbols.ATT_MINOCCURS);
              String maxOccurs = child.getAttribute(SchemaSymbols.ATT_MAXOCCURS);
              if (!((maxOccurs.length() == 0 || maxOccurs.equals("1"))
                  && (minOccurs.length() == 0 || minOccurs.equals("1")))) {
                reportSchemaError("src-redefine.6.1.2", new Object[]{ref}, child);
              }
            }
          }
        } // if ref was null some other stage of processing will flag the error
      }
    }
    return result;
  } // changeRedefineGroup

  // this method returns the XSDocumentInfo object that contains the
  // component corresponding to decl.  If components from this
  // document cannot be referred to from those of currSchema, this
  // method returns null; it's up to the caller to throw an error.
  // @param:  currSchema:  the XSDocumentInfo object containing the
  // decl ref'ing us.
  // @param:  decl:  the declaration being ref'd.
  // this method is superficial now. ---Jack
  private XSDocumentInfo findXSDocumentForDecl(XSDocumentInfo currSchema,
      Element decl, XSDocumentInfo decl_Doc) {

    if (DEBUG_NODE_POOL) {
      System.out.println("DOCUMENT NS:" + currSchema.fTargetNamespace + " hashcode:"
          + ((Object) currSchema.fSchemaElement).hashCode());
    }
    Object temp = decl_Doc;
    if (temp == null) {
      // something went badly wrong; we don't know this doc?
      return null;
    }
    XSDocumentInfo declDocInfo = (XSDocumentInfo) temp;
    return declDocInfo;
    /*********
     Logic here is unnecessary after schema WG's recent decision to allow
     schema components from one document to refer to components of any other,
     so long as there's some include/import/redefine path amongst them.
     If they rver reverse this decision the code's right here though...  - neilg
     // now look in fDependencyMap to see if this is reachable
     if(((Vector)fDependencyMap.get(currSchema)).contains(declDocInfo)) {
     return declDocInfo;
     }
     // obviously the requesting doc didn't include, redefine or
     // import the one containing decl...
     return null;
     **********/
  } // findXSDocumentForDecl(XSDocumentInfo, Element):  XSDocumentInfo

  // returns whether more than <annotation>s occur in children of elem
  private boolean nonAnnotationContent(Element elem) {
    for (Element child = DOMUtil.getFirstChildElement(elem); child != null;
        child = DOMUtil.getNextSiblingElement(child)) {
      if (!(DOMUtil.getLocalName(child).equals(SchemaSymbols.ELT_ANNOTATION))) {
        return true;
      }
    }
    return false;
  } // nonAnnotationContent(Element):  boolean

  private void setSchemasVisible(XSDocumentInfo startSchema) {
    if (DOMUtil.isHidden(startSchema.fSchemaElement, fHiddenNodes)) {
      // make it visible
      DOMUtil.setVisible(startSchema.fSchemaElement, fHiddenNodes);
      Vector dependingSchemas = (Vector) fDependencyMap.get(startSchema);
      for (int i = 0; i < dependingSchemas.size(); i++) {
        setSchemasVisible((XSDocumentInfo) dependingSchemas.elementAt(i));
      }
    }
    // if it's visible already than so must be its children
  } // setSchemasVisible(XSDocumentInfo): void

  private SimpleLocator xl = new SimpleLocator();

  /**
   * Extract location information from an Element node, and create a
   * new SimpleLocator object from such information. Returning null means
   * no information can be retrieved from the element.
   */
  public SimpleLocator element2Locator(Element e) {
    if (!(e instanceof ElementImpl)) {
      return null;
    }

    SimpleLocator l = new SimpleLocator();
    return element2Locator(e, l) ? l : null;
  }

  /**
   * Extract location information from an Element node, store such
   * information in the passed-in SimpleLocator object, then return
   * true. Returning false means can't extract or store such information.
   */
  public boolean element2Locator(Element e, SimpleLocator l) {
    if (l == null) {
      return false;
    }
    if (e instanceof ElementImpl) {
      ElementImpl ele = (ElementImpl) e;
      // get system id from document object
      Document doc = ele.getOwnerDocument();
      String sid = (String) fDoc2SystemId.get(DOMUtil.getRoot(doc));
      // line/column numbers are stored in the element node
      int line = ele.getLineNumber();
      int column = ele.getColumnNumber();
      l.setValues(sid, sid, line, column, ele.getCharacterOffset());
      return true;
    }
    return false;
  }

  private Element getElementFromMap(Map<String, Element> registry, String declKey) {
    if (registry == null) {
      return null;
    }
    return registry.get(declKey);
  }

  private XSDocumentInfo getDocInfoFromMap(Map<String, XSDocumentInfo> registry, String declKey) {
    if (registry == null) {
      return null;
    }
    return registry.get(declKey);
  }

  private Object getFromMap(Map registry, String key) {
    if (registry == null) {
      return null;
    }
    return registry.get(key);
  }

  void reportSchemaFatalError(String key, Object[] args, Element ele) {
    reportSchemaErr(key, args, ele, XMLErrorReporter.SEVERITY_FATAL_ERROR, null);
  }

  void reportSchemaError(String key, Object[] args, Element ele) {
    reportSchemaErr(key, args, ele, XMLErrorReporter.SEVERITY_ERROR, null);
  }

  void reportSchemaError(String key, Object[] args, Element ele, Exception exception) {
    reportSchemaErr(key, args, ele, XMLErrorReporter.SEVERITY_ERROR, exception);
  }

  void reportSchemaWarning(String key, Object[] args, Element ele) {
    reportSchemaErr(key, args, ele, XMLErrorReporter.SEVERITY_WARNING, null);
  }

  void reportSchemaWarning(String key, Object[] args, Element ele, Exception exception) {
    reportSchemaErr(key, args, ele, XMLErrorReporter.SEVERITY_WARNING, exception);
  }

  void reportSchemaErr(String key, Object[] args, Element ele, short type, Exception exception) {
    if (element2Locator(ele, xl)) {
      fErrorReporter.reportError(xl, XSMessageFormatter.SCHEMA_DOMAIN,
          key, args, type, exception);
    } else {
      fErrorReporter.reportError(XSMessageFormatter.SCHEMA_DOMAIN,
          key, args, type, exception);
    }
  }

  /**
   * Grammar pool used for validating annotations. This will return all of the
   * grammars from the grammar bucket. It will also return an object for the
   * schema for schemas which will contain at least the relevant declarations
   * for annotations.
   */
  private static class XSAnnotationGrammarPool implements XMLGrammarPool {

    private XSGrammarBucket fGrammarBucket;
    private Grammar[] fInitialGrammarSet;

    public Grammar[] retrieveInitialGrammarSet(String grammarType) {
      if (grammarType == XMLGrammarDescription.XML_SCHEMA) {
        if (fInitialGrammarSet == null) {
          if (fGrammarBucket == null) {
            fInitialGrammarSet = new Grammar[]{SchemaGrammar.Schema4Annotations.INSTANCE};
          } else {
            SchemaGrammar[] schemaGrammars = fGrammarBucket.getGrammars();
            /**
             * If the grammar bucket already contains the schema for schemas
             * then we already have the definitions for the parts relevant
             * to annotations.
             */
            for (int i = 0; i < schemaGrammars.length; ++i) {
              if (SchemaSymbols.URI_SCHEMAFORSCHEMA
                  .equals(schemaGrammars[i].getTargetNamespace())) {
                fInitialGrammarSet = schemaGrammars;
                return fInitialGrammarSet;
              }
            }
            Grammar[] grammars = new Grammar[schemaGrammars.length + 1];
            System.arraycopy(schemaGrammars, 0, grammars, 0, schemaGrammars.length);
            grammars[grammars.length - 1] = SchemaGrammar.Schema4Annotations.INSTANCE;
            fInitialGrammarSet = grammars;
          }
        }
        return fInitialGrammarSet;
      }
      return new Grammar[0];
    }

    public void cacheGrammars(String grammarType, Grammar[] grammars) {

    }

    public Grammar retrieveGrammar(XMLGrammarDescription desc) {
      if (desc.getGrammarType() == XMLGrammarDescription.XML_SCHEMA) {
        final String tns = ((XMLSchemaDescription) desc).getTargetNamespace();
        if (fGrammarBucket != null) {
          Grammar grammar = fGrammarBucket.getGrammar(tns);
          if (grammar != null) {
            return grammar;
          }
        }
        if (SchemaSymbols.URI_SCHEMAFORSCHEMA.equals(tns)) {
          return SchemaGrammar.Schema4Annotations.INSTANCE;
        }
      }
      return null;
    }

    public void refreshGrammars(XSGrammarBucket gBucket) {
      fGrammarBucket = gBucket;
      fInitialGrammarSet = null;
    }

    public void lockPool() {
    }

    public void unlockPool() {
    }

    public void clear() {
    }
  }

  /**
   * used to identify a reference to a schema document
   * if the same document is referenced twice with the same key, then
   * we only need to parse it once.
   *
   * When 2 XSDKey's are compared, the following table can be used to
   * determine whether they are equal:
   * inc     red     imp     pre     ins
   * inc  N/L      ?      N/L     N/L     N/L
   * red   ?      N/L      ?       ?       ?
   * imp  N/L      ?      N/P     N/P     N/P
   * pre  N/L      ?      N/P     N/P     N/P
   * ins  N/L      ?      N/P     N/P     N/P
   *
   * Where: N/L: duplicate when they have the same namespace and location.
   * ? : not clear from the spec.
   * REVISIT: to simplify the process, also considering
   * it's very rare, we treat them as not duplicate.
   * N/P: not possible. imp/pre/ins are referenced by namespace.
   * when the first time we encounter a schema document for a
   * namespace, we create a grammar and store it in the grammar
   * bucket. when we see another reference to the same namespace,
   * we first check whether a grammar with the same namespace is
   * already in the bucket, which is true in this case, so we
   * won't create another XSDKey.
   *
   * Conclusion from the table: two XSDKey's are duplicate only when all of
   * the following are true:
   * 1. They are both "redefine", or neither is "redefine";
   * 2. They have the same namespace;
   * 3. They have the same non-null location.
   *
   * About 3: if neither has a non-null location, then it's the case where
   * 2 input streams are provided, but no system ID is provided. We can't tell
   * whether the 2 streams have the same content, so we treat them as not
   * duplicate.
   */
  private static class XSDKey {

    String systemId;
    short referType;
    // for inclue/redefine, this is the enclosing namespace
    // for import/preparse/instance, this is the target namespace
    String referNS;

    XSDKey(String systemId, short referType, String referNS) {
      this.systemId = systemId;
      this.referType = referType;
      this.referNS = referNS;
    }

    public int hashCode() {
      // according to the description at the beginning of this class,
      // we use the hashcode of the namespace as the hashcoe of this key.
      return referNS == null ? 0 : referNS.hashCode();
    }

    public boolean equals(Object obj) {
      if (!(obj instanceof XSDKey)) {
        return false;
      }
      XSDKey key = (XSDKey) obj;

      // condition 1: both are redefine
      /** if (referType == XSDDescription.CONTEXT_REDEFINE ||
       key.referType == XSDDescription.CONTEXT_REDEFINE) {
       if (referType != key.referType)
       return false;
       }**/

      // condition 2: same namespace
      if (referNS != key.referNS) {
        return false;
      }

      // condition 3: same non-null location
      if (systemId == null || !systemId.equals(key.systemId)) {
        return false;
      }

      return true;
    }
  }

  private static final class SAX2XNIUtil extends ErrorHandlerWrapper {

    public static XMLParseException createXMLParseException0(SAXParseException exception) {
      return createXMLParseException(exception);
    }

    public static XNIException createXNIException0(SAXException exception) {
      return createXNIException(exception);
    }
  }

  /**
   * @param state
   */
  public void setGenerateSyntheticAnnotations(boolean state) {
    fSchemaParser.setFeature(GENERATE_SYNTHETIC_ANNOTATIONS, state);
  }

} // XSDHandler
